diff options
271 files changed, 12304 insertions, 5425 deletions
diff --git a/Android.bp b/Android.bp index 157bdcbec6bb..fb52297343b6 100644 --- a/Android.bp +++ b/Android.bp @@ -112,6 +112,7 @@ filegroup { ":framework_native_aidl", ":gatekeeper_aidl", ":gsiservice_aidl", + ":guiconstants_aidl", ":idmap2_aidl", ":idmap2_core_aidl", ":incidentcompanion_aidl", diff --git a/core/api/current.txt b/core/api/current.txt index cfc4ea995af5..b0a95c46d36c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -31072,8 +31072,8 @@ package android.os { ctor public Environment(); method public static java.io.File getDataDirectory(); method public static java.io.File getDownloadCacheDirectory(); - method @Deprecated public static java.io.File getExternalStorageDirectory(); - method @Deprecated public static java.io.File getExternalStoragePublicDirectory(String); + method public static java.io.File getExternalStorageDirectory(); + method public static java.io.File getExternalStoragePublicDirectory(String); method public static String getExternalStorageState(); method public static String getExternalStorageState(java.io.File); method @NonNull public static java.io.File getRootDirectory(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e876a36fc0b0..ac02fe651892 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1271,6 +1271,12 @@ package android.hardware.soundtrigger { package android.inputmethodservice { + public abstract class AbstractInputMethodService extends android.window.WindowProviderService implements android.view.KeyEvent.Callback { + method public final int getInitialDisplayId(); + method @Nullable public final android.os.Bundle getWindowContextOptions(); + method public final int getWindowType(); + } + @UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService { field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L } @@ -2110,6 +2116,7 @@ package android.provider { public final class DeviceConfig { field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager"; field public static final String NAMESPACE_ANDROID = "android"; + field public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides"; field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis"; field public static final String NAMESPACE_DEVICE_IDLE = "device_idle"; field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler"; @@ -3246,6 +3253,7 @@ package android.window { @UiContext public abstract class WindowProviderService extends android.app.Service { ctor public WindowProviderService(); method public final void attachToWindowToken(@NonNull android.os.IBinder); + method @NonNull public int getInitialDisplayId(); method @Nullable public android.os.Bundle getWindowContextOptions(); method public abstract int getWindowType(); } diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index b31d8f70a7d0..765a2ba45079 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -16,6 +16,8 @@ package android.accessibilityservice; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; + import android.accessibilityservice.GestureDescription.MotionEventGenerator; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; @@ -27,6 +29,7 @@ import android.annotation.TestApi; import android.app.Service; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.ContextWrapper; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; @@ -36,6 +39,7 @@ import android.graphics.Region; import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManager; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -961,30 +965,31 @@ public abstract class AccessibilityService extends Service { } } + @NonNull @Override public Context createDisplayContext(Display display) { - final Context context = super.createDisplayContext(display); - final int displayId = display.getDisplayId(); - setDefaultTokenInternal(context, displayId); - return context; + return new AccessibilityContext(super.createDisplayContext(display), mConnectionId); } - private void setDefaultTokenInternal(Context context, int displayId) { - final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE); - final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); - IBinder token = null; - if (connection != null) { - synchronized (mLock) { - try { - token = connection.getOverlayWindowToken(displayId); - } catch (RemoteException re) { - Log.w(LOG_TAG, "Failed to get window token", re); - re.rethrowFromSystemServer(); - } - } - wm.setDefaultToken(token); + @NonNull + @Override + public Context createWindowContext(int type, @Nullable Bundle options) { + final Context context = super.createWindowContext(type, options); + if (type != TYPE_ACCESSIBILITY_OVERLAY) { + return context; + } + return new AccessibilityContext(context, mConnectionId); + } + + @NonNull + @Override + public Context createWindowContext(@NonNull Display display, int type, + @Nullable Bundle options) { + final Context context = super.createWindowContext(display, type, options); + if (type != TYPE_ACCESSIBILITY_OVERLAY) { + return context; } + return new AccessibilityContext(context, mConnectionId); } /** @@ -2069,6 +2074,10 @@ public abstract class AccessibilityService extends Service { if (WINDOW_SERVICE.equals(name)) { if (mWindowManager == null) { mWindowManager = (WindowManager) getBaseContext().getSystemService(name); + final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager; + // Set e default token obtained from the connection to ensure client could use + // accessibility overlay. + wm.setDefaultToken(mWindowToken); } return mWindowManager; } @@ -2177,8 +2186,10 @@ public abstract class AccessibilityService extends Service { // The client may have already obtained the window manager, so // update the default token on whatever manager we gave them. - final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE); - wm.setDefaultToken(windowToken); + if (mWindowManager != null) { + final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager; + wm.setDefaultToken(mWindowToken); + } } @Override @@ -2675,4 +2686,58 @@ public abstract class AccessibilityService extends Service { } } } + + private static class AccessibilityContext extends ContextWrapper { + private final int mConnectionId; + + private AccessibilityContext(Context base, int connectionId) { + super(base); + mConnectionId = connectionId; + setDefaultTokenInternal(this, getDisplayId()); + } + + @NonNull + @Override + public Context createDisplayContext(Display display) { + return new AccessibilityContext(super.createDisplayContext(display), mConnectionId); + } + + @NonNull + @Override + public Context createWindowContext(int type, @Nullable Bundle options) { + final Context context = super.createWindowContext(type, options); + if (type != TYPE_ACCESSIBILITY_OVERLAY) { + return context; + } + return new AccessibilityContext(context, mConnectionId); + } + + @NonNull + @Override + public Context createWindowContext(@NonNull Display display, int type, + @Nullable Bundle options) { + final Context context = super.createWindowContext(display, type, options); + if (type != TYPE_ACCESSIBILITY_OVERLAY) { + return context; + } + return new AccessibilityContext(context, mConnectionId); + } + + private void setDefaultTokenInternal(Context context, int displayId) { + final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService( + WINDOW_SERVICE); + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getConnection(mConnectionId); + IBinder token = null; + if (connection != null) { + try { + token = connection.getOverlayWindowToken(displayId); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Failed to get window token", re); + re.rethrowFromSystemServer(); + } + wm.setDefaultToken(token); + } + } + } } diff --git a/core/java/android/accessibilityservice/AccessibilityTrace.java b/core/java/android/accessibilityservice/AccessibilityTrace.java new file mode 100644 index 000000000000..f28015ab4685 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityTrace.java @@ -0,0 +1,216 @@ +/** + * Copyright (C) 2021 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 android.accessibilityservice; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Interface to log accessibility trace. + * + * @hide + */ +public interface AccessibilityTrace { + String NAME_ACCESSIBILITY_SERVICE_CONNECTION = "IAccessibilityServiceConnection"; + String NAME_ACCESSIBILITY_SERVICE_CLIENT = "IAccessibilityServiceClient"; + String NAME_ACCESSIBILITY_MANAGER = "IAccessibilityManager"; + String NAME_ACCESSIBILITY_MANAGER_CLIENT = "IAccessibilityManagerClient"; + String NAME_ACCESSIBILITY_INTERACTION_CONNECTION = "IAccessibilityInteractionConnection"; + String NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = + "IAccessibilityInteractionConnectionCallback"; + String NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = "IRemoteMagnificationAnimationCallback"; + String NAME_WINDOW_MAGNIFICATION_CONNECTION = "IWindowMagnificationConnection"; + String NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = "IWindowMagnificationConnectionCallback"; + String NAME_WINDOW_MANAGER_INTERNAL = "WindowManagerInternal"; + String NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = "WindowsForAccessibilityCallback"; + String NAME_MAGNIFICATION_CALLBACK = "MagnificationCallbacks"; + String NAME_INPUT_FILTER = "InputFilter"; + String NAME_GESTURE = "Gesture"; + String NAME_ACCESSIBILITY_SERVICE = "AccessibilityService"; + String NAME_PACKAGE_BROADCAST_RECEIVER = "PMBroadcastReceiver"; + String NAME_USER_BROADCAST_RECEIVER = "UserBroadcastReceiver"; + String NAME_FINGERPRINT = "FingerprintGesture"; + String NAME_ACCESSIBILITY_INTERACTION_CLIENT = "AccessibilityInteractionClient"; + + String NAME_ALL_LOGGINGS = "AllLoggings"; + String NAME_NONE = "None"; + + long FLAGS_ACCESSIBILITY_SERVICE_CONNECTION = 0x0000000000000001L; + long FLAGS_ACCESSIBILITY_SERVICE_CLIENT = 0x0000000000000002L; + long FLAGS_ACCESSIBILITY_MANAGER = 0x0000000000000004L; + long FLAGS_ACCESSIBILITY_MANAGER_CLIENT = 0x0000000000000008L; + long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION = 0x0000000000000010L; + long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = 0x0000000000000020L; + long FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = 0x0000000000000040L; + long FLAGS_WINDOW_MAGNIFICATION_CONNECTION = 0x0000000000000080L; + long FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = 0x0000000000000100L; + long FLAGS_WINDOW_MANAGER_INTERNAL = 0x0000000000000200L; + long FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = 0x0000000000000400L; + long FLAGS_MAGNIFICATION_CALLBACK = 0x0000000000000800L; + long FLAGS_INPUT_FILTER = 0x0000000000001000L; + long FLAGS_GESTURE = 0x0000000000002000L; + long FLAGS_ACCESSIBILITY_SERVICE = 0x0000000000004000L; + long FLAGS_PACKAGE_BROADCAST_RECEIVER = 0x0000000000008000L; + long FLAGS_USER_BROADCAST_RECEIVER = 0x0000000000010000L; + long FLAGS_FINGERPRINT = 0x0000000000020000L; + long FLAGS_ACCESSIBILITY_INTERACTION_CLIENT = 0x0000000000040000L; + + long FLAGS_LOGGING_NONE = 0x0000000000000000L; + long FLAGS_LOGGING_ALL = 0xFFFFFFFFFFFFFFFFL; + + long FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES = FLAGS_ACCESSIBILITY_INTERACTION_CLIENT + | FLAGS_ACCESSIBILITY_SERVICE + | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION + | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK; + + Map<String, Long> sNamesToFlags = Map.ofEntries( + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_SERVICE_CONNECTION, FLAGS_ACCESSIBILITY_SERVICE_CONNECTION), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_SERVICE_CLIENT, FLAGS_ACCESSIBILITY_SERVICE_CLIENT), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_MANAGER, FLAGS_ACCESSIBILITY_MANAGER), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_MANAGER_CLIENT, FLAGS_ACCESSIBILITY_MANAGER_CLIENT), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_INTERACTION_CONNECTION, + FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK, + FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK), + new AbstractMap.SimpleEntry<String, Long>( + NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, + FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK), + new AbstractMap.SimpleEntry<String, Long>( + NAME_WINDOW_MAGNIFICATION_CONNECTION, FLAGS_WINDOW_MAGNIFICATION_CONNECTION), + new AbstractMap.SimpleEntry<String, Long>( + NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK), + new AbstractMap.SimpleEntry<String, Long>( + NAME_WINDOW_MANAGER_INTERNAL, FLAGS_WINDOW_MANAGER_INTERNAL), + new AbstractMap.SimpleEntry<String, Long>( + NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK), + new AbstractMap.SimpleEntry<String, Long>( + NAME_MAGNIFICATION_CALLBACK, FLAGS_MAGNIFICATION_CALLBACK), + new AbstractMap.SimpleEntry<String, Long>(NAME_INPUT_FILTER, FLAGS_INPUT_FILTER), + new AbstractMap.SimpleEntry<String, Long>(NAME_GESTURE, FLAGS_GESTURE), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_SERVICE, FLAGS_ACCESSIBILITY_SERVICE), + new AbstractMap.SimpleEntry<String, Long>( + NAME_PACKAGE_BROADCAST_RECEIVER, FLAGS_PACKAGE_BROADCAST_RECEIVER), + new AbstractMap.SimpleEntry<String, Long>( + NAME_USER_BROADCAST_RECEIVER, FLAGS_USER_BROADCAST_RECEIVER), + new AbstractMap.SimpleEntry<String, Long>(NAME_FINGERPRINT, FLAGS_FINGERPRINT), + new AbstractMap.SimpleEntry<String, Long>( + NAME_ACCESSIBILITY_INTERACTION_CLIENT, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT), + new AbstractMap.SimpleEntry<String, Long>(NAME_NONE, FLAGS_LOGGING_NONE), + new AbstractMap.SimpleEntry<String, Long>(NAME_ALL_LOGGINGS, FLAGS_LOGGING_ALL)); + + /** + * Get the flags of the logging types by the given names. + * The names list contains logging type names in lower case. + */ + static long getLoggingFlagsFromNames(List<String> names) { + long types = FLAGS_LOGGING_NONE; + for (String name : names) { + long flag = sNamesToFlags.get(name); + types |= flag; + } + return types; + } + + /** + * Get the list of the names of logging types by the given flags. + */ + static List<String> getNamesOfLoggingTypes(long flags) { + List<String> list = new ArrayList<String>(); + + for (Map.Entry<String, Long> entry : sNamesToFlags.entrySet()) { + if ((entry.getValue() & flags) != FLAGS_LOGGING_NONE) { + list.add(entry.getKey()); + } + } + + return list; + } + + /** + * Whether the trace is enabled for any logging type. + */ + boolean isA11yTracingEnabled(); + + /** + * Whether the trace is enabled for any of the given logging type. + */ + boolean isA11yTracingEnabledForTypes(long typeIdFlags); + + /** + * Get trace state to be sent to AccessibilityManager. + */ + int getTraceStateForAccessibilityManagerClientState(); + + /** + * Start tracing for the given logging types. + */ + void startTrace(long flagss); + + /** + * Stop tracing. + */ + void stopTrace(); + + /** + * Log one trace entry. + * @param where A string to identify this log entry, which can be used to search through the + * tracing file. + * @param loggingFlags Flags to identify which logging types this entry belongs to. This + * can be used to filter the log entries when generating tracing file. + */ + void logTrace(String where, long loggingFlags); + + /** + * Log one trace entry. + * @param where A string to identify this log entry, which can be used to filter/search + * through the tracing file. + * @param loggingFlags Flags to identify which logging types this entry belongs to. This + * can be used to filter the log entries when generating tracing file. + * @param callingParams The parameters for the method to be logged. + */ + void logTrace(String where, long loggingFlags, String callingParams); + + /** + * Log one trace entry. Accessibility services using AccessibilityInteractionClient to + * make screen content related requests use this API to log entry when receive callback. + * @param timestamp The timestamp when a callback is received. + * @param where A string to identify this log entry, which can be used to filter/search + * through the tracing file. + * @param loggingFlags Flags to identify which logging types this entry belongs to. This + * can be used to filter the log entries when generating tracing file. + * @param callingParams The parameters for the callback. + * @param processId The process id of the calling component. + * @param threadId The threadId of the calling component. + * @param callingUid The calling uid of the callback. + * @param callStack The call stack of the callback. + * @param ignoreStackElements ignore these call stack element + */ + void logTrace(long timestamp, String where, long loggingFlags, String callingParams, + int processId, long threadId, int callingUid, StackTraceElement[] callStack, + Set<String> ignoreStackElements); +} diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 923b6f41414a..1e76bbf43370 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -118,6 +118,6 @@ interface IAccessibilityServiceConnection { void setFocusAppearance(int strokeWidth, int color); - oneway void logTrace(long timestamp, String where, String callingParams, int processId, - long threadId, int callingUid, in Bundle serializedCallingStackInBundle); + oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams, + int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle); } diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 4a7fcd232ce9..a83662592513 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -476,6 +476,19 @@ public class ActivityTaskManager { } /** + * Detaches the navigation bar from the app it was attached to during a transition. + * @hide + */ + @RequiresPermission(android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) + public void detachNavigationBarFromApp(@NonNull IBinder transition) { + try { + getService().detachNavigationBarFromApp(transition); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Information you can retrieve about a root task in the system. * @hide */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3915abe1e9eb..0a73e6c78bb0 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -18,7 +18,6 @@ package android.app; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull; -import static android.app.ConfigurationController.freeTextLayoutCachesIfNeeded; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; @@ -31,6 +30,10 @@ import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS; import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; import static android.view.Display.INVALID_DISPLAY; +import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets; +import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; +import static android.window.ConfigurationHelper.isDifferentDisplay; +import static android.window.ConfigurationHelper.shouldUpdateResources; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; @@ -88,10 +91,8 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.HardwareRenderer; -import android.graphics.Rect; import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; -import android.inputmethodservice.InputMethodService; import android.media.MediaFrameworkInitializer; import android.media.MediaFrameworkPlatformInitializer; import android.media.MediaServiceManager; @@ -183,6 +184,7 @@ import android.webkit.WebView; import android.window.SizeConfigurationBuckets; import android.window.SplashScreen; import android.window.SplashScreenView; +import android.window.WindowProviderService; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -5751,7 +5753,7 @@ public final class ActivityThread extends ClientTransactionHandler } @Override - public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) { + public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) { ArrayList<ComponentCallbacks2> callbacks = new ArrayList<ComponentCallbacks2>(); @@ -5760,7 +5762,7 @@ public final class ActivityThread extends ClientTransactionHandler for (int i=0; i<NAPP; i++) { callbacks.add(mAllApplications.get(i)); } - if (includeActivities) { + if (includeUiContexts) { for (int i = mActivities.size() - 1; i >= 0; i--) { final Activity a = mActivities.valueAt(i).activity; if (a != null && !a.mFinished) { @@ -5770,11 +5772,12 @@ public final class ActivityThread extends ClientTransactionHandler } final int NSVC = mServices.size(); for (int i=0; i<NSVC; i++) { - final ComponentCallbacks2 serviceComp = mServices.valueAt(i); - if (serviceComp instanceof InputMethodService) { - mHasImeComponent = true; + final Service service = mServices.valueAt(i); + // If {@code includeUiContext} is set to false, WindowProviderService should not be + // collected because WindowProviderService is a UI Context. + if (includeUiContexts || !(service instanceof WindowProviderService)) { + callbacks.add(service); } - callbacks.add(serviceComp); } } synchronized (mProviderMap) { @@ -5829,35 +5832,26 @@ public final class ActivityThread extends ClientTransactionHandler // change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition handleWindowingModeChangeIfNeeded(activity, newConfig); - final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId); - boolean shouldReportChange = false; - if (activity.mCurrentConfig == null) { - shouldReportChange = true; - } else { - // If the new config is the same as the config this Activity is already running with and - // the override config also didn't change, then don't bother calling - // onConfigurationChanged. - // TODO(b/173090263): Use diff instead after the improvement of AssetManager and - // ResourcesImpl constructions. - int diff = activity.mCurrentConfig.diffPublicOnly(newConfig); - final ActivityClientRecord cr = getActivityClient(activityToken); - diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig, - cr != null ? cr.mSizeConfigurations : null); - - if (diff == 0) { - if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig) - && !movedToDifferentDisplay - && mResourcesManager.isSameResourcesOverrideConfig( - activityToken, amOverrideConfig)) { - // Nothing significant, don't proceed with updating and reporting. - return null; - } - } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) { + final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(), + displayId); + final SizeConfigurationBuckets buckets = getActivityClient(activityToken) + .mSizeConfigurations; + final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig, + newConfig, buckets); + final boolean hasPublicConfigChange = diff != 0; + // TODO(b/173090263): Use diff instead after the improvement of AssetManager and + // ResourcesImpl constructions. + final boolean shouldUpdateResources = hasPublicConfigChange + || shouldUpdateResources(activityToken, activity.mCurrentConfig, newConfig, + amOverrideConfig, movedToDifferentDisplay, hasPublicConfigChange); + final boolean shouldReportChange = hasPublicConfigChange // If this activity doesn't handle any of the config changes, then don't bother // calling onConfigurationChanged. Otherwise, report to the activity for the // changes. - shouldReportChange = true; - } + && (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0; + // Nothing significant, don't proceed with updating and reporting. + if (!shouldUpdateResources) { + return null; } // Propagate the configuration change to ResourcesManager and Activity. @@ -5908,26 +5902,6 @@ public final class ActivityThread extends ClientTransactionHandler return configToReport; } - // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl - // constructions. - /** - * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs - * should be updated. - * - * @see WindowManager#getCurrentWindowMetrics() - * @see WindowManager#getMaximumWindowMetrics() - */ - private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig, - @NonNull Configuration newConfig) { - final Rect currentBounds = currentConfig.windowConfiguration.getBounds(); - final Rect newBounds = newConfig.windowConfiguration.getBounds(); - - final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds(); - final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds(); - - return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds); - } - public final void applyConfigurationToResources(Configuration config) { synchronized (mResourcesManager) { mResourcesManager.applyConfigurationToResources(config, null); @@ -6071,7 +6045,8 @@ public final class ActivityThread extends ClientTransactionHandler // display. displayId = r.activity.getDisplayId(); } - final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId); + final boolean movedToDifferentDisplay = isDifferentDisplay( + r.activity.getDisplayId(), displayId); if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig) && !movedToDifferentDisplay) { if (DEBUG_CONFIGURATION) { @@ -6107,14 +6082,6 @@ public final class ActivityThread extends ClientTransactionHandler mSomeActivitiesChanged = true; } - /** - * Checks if the display id of activity is different from the given one. Note that - * {@link Display#INVALID_DISPLAY} means no difference. - */ - private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) { - return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId(); - } - final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { if (start) { try { @@ -6294,7 +6261,7 @@ public final class ActivityThread extends ClientTransactionHandler final void handleLowMemory() { final ArrayList<ComponentCallbacks2> callbacks = - collectComponentCallbacks(true /* includeActivities */); + collectComponentCallbacks(true /* includeUiContexts */); final int N = callbacks.size(); for (int i=0; i<N; i++) { @@ -6327,7 +6294,7 @@ public final class ActivityThread extends ClientTransactionHandler } final ArrayList<ComponentCallbacks2> callbacks = - collectComponentCallbacks(true /* includeActivities */); + collectComponentCallbacks(true /* includeUiContexts */); final int N = callbacks.size(); for (int i = 0; i < N; i++) { @@ -7546,12 +7513,6 @@ public final class ActivityThread extends ClientTransactionHandler ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> { synchronized (mResourcesManager) { - // TODO (b/135719017): Temporary log for debugging IME service. - if (Build.IS_DEBUGGABLE && mHasImeComponent) { - Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, " - + "config=" + globalConfig); - } - // We need to apply this change to the resources immediately, because upon returning // the view hierarchy will be informed about it. if (mResourcesManager.applyConfigurationToResources(globalConfig, @@ -7906,11 +7867,6 @@ public final class ActivityThread extends ClientTransactionHandler return mDensityCompatMode; } - @Override - public boolean hasImeComponent() { - return mHasImeComponent; - } - // ------------------ Regular JNI ------------------------ private native void nPurgePendingResources(); private native void nDumpGraphicsInfo(FileDescriptor fd); diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java index d91933c0f817..bc698f657305 100644 --- a/core/java/android/app/ActivityThreadInternal.java +++ b/core/java/android/app/ActivityThreadInternal.java @@ -32,11 +32,9 @@ interface ActivityThreadInternal { boolean isInDensityCompatMode(); - boolean hasImeComponent(); - boolean isCachedProcessState(); Application getApplication(); - ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities); + ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts); } diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index f79e0780ecae..8637e31eb122 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -17,24 +17,20 @@ package android.app; import static android.app.ActivityThread.DEBUG_CONFIGURATION; +import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentCallbacks2; import android.content.Context; -import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.HardwareRenderer; -import android.inputmethodservice.InputMethodService; -import android.os.Build; import android.os.LocaleList; import android.os.Trace; import android.util.DisplayMetrics; -import android.util.Log; import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.WindowManagerGlobal; @@ -169,12 +165,7 @@ class ConfigurationController { mPendingConfiguration = null; } - final boolean hasIme = mActivityThread.hasImeComponent(); if (config == null) { - // TODO (b/135719017): Temporary log for debugging IME service. - if (Build.IS_DEBUGGABLE && hasIme) { - Log.w(TAG, "handleConfigurationChanged for IME app but config is null"); - } return; } @@ -205,12 +196,6 @@ class ConfigurationController { mConfiguration = new Configuration(); } if (!mConfiguration.isOtherSeqNewer(config) && compat == null) { - // TODO (b/135719017): Temporary log for debugging IME service. - if (Build.IS_DEBUGGABLE && hasIme) { - Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete " - + ", config=" + config - + ", mConfiguration=" + mConfiguration); - } return; } @@ -228,7 +213,7 @@ class ConfigurationController { } final ArrayList<ComponentCallbacks2> callbacks = - mActivityThread.collectComponentCallbacks(false /* includeActivities */); + mActivityThread.collectComponentCallbacks(false /* includeUiContexts */); freeTextLayoutCachesIfNeeded(configDiff); @@ -238,13 +223,6 @@ class ConfigurationController { ComponentCallbacks2 cb = callbacks.get(i); if (!equivalent) { performConfigurationChanged(cb, config); - } else { - // TODO (b/135719017): Temporary log for debugging IME service. - if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) { - Log.w(TAG, "performConfigurationChanged didn't callback to IME " - + ", configDiff=" + configDiff - + ", mConfiguration=" + mConfiguration); - } } } } @@ -326,16 +304,4 @@ class ConfigurationController { return newConfig; } - /** Ask test layout engine to free its caches if there is a locale change. */ - static void freeTextLayoutCachesIfNeeded(int configDiff) { - if (configDiff != 0) { - boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0); - if (hasLocaleConfigChange) { - Canvas.freeTextLayoutCaches(); - if (DEBUG_CONFIGURATION) { - Slog.v(TAG, "Cleared TextLayout Caches"); - } - } - } - } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5e99c79a7497..64963031f1b3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -3186,12 +3186,6 @@ class ContextImpl extends Context { @UnsupportedAppUsage final void setOuterContext(@NonNull Context context) { mOuterContext = context; - // TODO(b/149463653): check if we still need this method after migrating IMS to - // WindowContext. - if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) { - mContextType = CONTEXT_TYPE_WINDOW_CONTEXT; - mIsConfigurationBasedContext = true; - } } @UnsupportedAppUsage diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 74d51a0bcf63..9f97fade6e98 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -330,4 +330,10 @@ interface IActivityTaskManager { * When the Picture-in-picture state has changed. */ void onPictureInPictureStateChanged(in PictureInPictureUiState pipState); + + /** + * Re-attach navbar to the display during a recents transition. + * TODO(188595497): Remove this once navbar attachment is in shell. + */ + void detachNavigationBarFromApp(in IBinder transition); } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 20afffc1f562..af75c6910fe1 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -693,7 +693,7 @@ public class ResourcesManager { * @return true if activity resources override config matches the provided one or they are both * null, false otherwise. */ - boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken, + public boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken, @Nullable Configuration overrideConfig) { synchronized (mLock) { final ActivityResources activityResources diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index b95412f00453..3d32e00185ed 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -17,6 +17,7 @@ package android.app; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -117,6 +118,12 @@ public class TaskInfo { public int displayId; /** + * The feature id of {@link com.android.server.wm.TaskDisplayArea} this task is associated with. + * @hide + */ + public int displayAreaFeatureId = FEATURE_UNDEFINED; + + /** * The recent activity values for the highest activity in the stack to have set the values. * {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}. */ @@ -239,6 +246,12 @@ public class TaskInfo { */ public boolean isVisible; + /** + * Whether this task is sleeping due to sleeping display. + * @hide + */ + public boolean isSleeping; + TaskInfo() { // Do nothing } @@ -325,11 +338,10 @@ public class TaskInfo { } /** - * Returns {@code true} if parameters that are important for task organizers have changed - * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners - * about that. - * @hide - */ + * Returns {@code true} if the parameters that are important for task organizers are equal + * between this {@link TaskInfo} and {@param that}. + * @hide + */ public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) { if (that == null) { return false; @@ -337,12 +349,14 @@ public class TaskInfo { return topActivityType == that.topActivityType && isResizeable == that.isResizeable && supportsMultiWindow == that.supportsMultiWindow + && displayAreaFeatureId == that.displayAreaFeatureId && Objects.equals(positionInParent, that.positionInParent) && Objects.equals(pictureInPictureParams, that.pictureInPictureParams) && getWindowingMode() == that.getWindowingMode() && Objects.equals(taskDescription, that.taskDescription) && isFocused == that.isFocused - && isVisible == that.isVisible; + && isVisible == that.isVisible + && isSleeping == that.isSleeping; } /** @@ -396,9 +410,11 @@ public class TaskInfo { parentTaskId = source.readInt(); isFocused = source.readBoolean(); isVisible = source.readBoolean(); + isSleeping = source.readBoolean(); topActivityToken = source.readStrongBinder(); topActivityInSizeCompat = source.readBoolean(); mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR); + displayAreaFeatureId = source.readInt(); } /** @@ -434,9 +450,11 @@ public class TaskInfo { dest.writeInt(parentTaskId); dest.writeBoolean(isFocused); dest.writeBoolean(isVisible); + dest.writeBoolean(isSleeping); dest.writeStrongBinder(topActivityToken); dest.writeBoolean(topActivityInSizeCompat); dest.writeTypedObject(mTopActivityLocusId, flags); + dest.writeInt(displayAreaFeatureId); } @Override @@ -462,9 +480,11 @@ public class TaskInfo { + " parentTaskId=" + parentTaskId + " isFocused=" + isFocused + " isVisible=" + isVisible + + " isSleeping=" + isSleeping + " topActivityToken=" + topActivityToken + " topActivityInSizeCompat=" + topActivityInSizeCompat - + " locusId= " + mTopActivityLocusId + + " locusId=" + mTopActivityLocusId + + " displayAreaFeatureId=" + displayAreaFeatureId + "}"; } } diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java index fad6cd311021..ebc2945fb1a0 100644 --- a/core/java/android/app/compat/PackageOverride.java +++ b/core/java/android/app/compat/PackageOverride.java @@ -24,6 +24,7 @@ import android.os.Parcel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * An app compat override applied to a given package and change id pairing. @@ -139,6 +140,22 @@ public final class PackageOverride { /** @hide */ @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PackageOverride that = (PackageOverride) o; + return mMinVersionCode == that.mMinVersionCode && mMaxVersionCode == that.mMaxVersionCode + && mEnabled == that.mEnabled; + } + + /** @hide */ + @Override + public int hashCode() { + return Objects.hash(mMinVersionCode, mMaxVersionCode, mEnabled); + } + + /** @hide */ + @Override public String toString() { if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) { return Boolean.toString(mEnabled); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 723be5868eaf..60cde5943655 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3055,9 +3055,6 @@ public final class BluetoothAdapter { return true; } return false; - } else if (profile == BluetoothProfile.LE_AUDIO) { - BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); - return true; } else if (profile == BluetoothProfile.VOLUME_CONTROL) { BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this); return true; @@ -3150,10 +3147,6 @@ public final class BluetoothAdapter { BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); break; - case BluetoothProfile.LE_AUDIO: - BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy; - leAudio.close(); - break; case BluetoothProfile.VOLUME_CONTROL: BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy; vcs.close(); diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 3cd13a212a4b..17d4ae6205ca 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -18,16 +18,23 @@ package android.inputmethodservice; import android.annotation.MainThread; import android.annotation.NonNull; -import android.app.Service; +import android.annotation.Nullable; +import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSession; +import android.window.WindowProviderService; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -44,9 +51,22 @@ import java.io.PrintWriter; * implement. This base class takes care of reporting your InputMethod from * the service when clients bind to it, but provides no standard implementation * of the InputMethod interface itself. Derived classes must implement that - * interface. + * interface.</p> + * + * <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft + * input may not be the entire screen. For example, some devices may support to show the soft input + * on only half of screen.</p> + * + * <p>In that case, moving the soft input from one half screen to another will trigger a + * {@link android.content.res.Resources} update to match the new {@link Configuration} and + * this {@link AbstractInputMethodService} may also receive a + * {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes + * </p> + * + * @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration) + * @see Context#isUiContext Context#isUiContext to see the concept of UI Context. */ -public abstract class AbstractInputMethodService extends Service +public abstract class AbstractInputMethodService extends WindowProviderService implements KeyEvent.Callback { private InputMethod mInputMethod; @@ -272,9 +292,33 @@ public abstract class AbstractInputMethodService extends Service public void notifyUserActionIfNecessary() { } + // TODO(b/149463653): remove it in T. We missed the API deadline in S. /** @hide */ @Override public final boolean isUiContext() { return true; } + + /** @hide */ + @Override + public final int getWindowType() { + return WindowManager.LayoutParams.TYPE_INPUT_METHOD; + } + + /** @hide */ + @Override + @Nullable + public final Bundle getWindowContextOptions() { + return null; + } + + /** @hide */ + @Override + public final int getInitialDisplayId() { + try { + return WindowManagerGlobal.getWindowManagerService().getImeDisplayId(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 9198eb74d1f8..89612fe753df 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -170,8 +170,8 @@ class IInputMethodWrapper extends IInputMethod.Stub case DO_INITIALIZE_INTERNAL: { SomeArgs args = (SomeArgs) msg.obj; try { - inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1, - (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3); + inputMethod.initializeInternal((IBinder) args.arg1, + (IInputMethodPrivilegedOperations) args.arg2, msg.arg1); } finally { args.recycle(); } @@ -279,11 +279,10 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override - public void initializeInternal(IBinder token, int displayId, - IInputMethodPrivilegedOperations privOps, int configChanges) { + public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps, + int configChanges) { mCaller.executeOrSendMessage( - mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps, - configChanges)); + mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, configChanges, token, privOps)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 881e0cfb58d7..42fa9fbc7b94 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -589,7 +589,7 @@ public class InputMethodService extends AbstractInputMethodService { */ @MainThread @Override - public final void initializeInternal(@NonNull IBinder token, int displayId, + public final void initializeInternal(@NonNull IBinder token, IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) { Log.w(TAG, "The token has already registered, ignore this initialization."); @@ -599,7 +599,6 @@ public class InputMethodService extends AbstractInputMethodService { mConfigTracker.onInitialize(configChanges); mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); - updateInputMethodDisplay(displayId); attachToken(token); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -629,29 +628,13 @@ public class InputMethodService extends AbstractInputMethodService { throw new IllegalStateException( "attachToken() must be called at most once. token=" + token); } + attachToWindowToken(token); mToken = token; mWindow.setToken(token); } /** * {@inheritDoc} - * @hide - */ - @MainThread - @Override - public void updateInputMethodDisplay(int displayId) { - if (getDisplayId() == displayId) { - return; - } - // Update display for adding IME window to the right display. - // TODO(b/111364446) Need to address context lifecycle issue if need to re-create - // for update resources & configuration correctly when show soft input - // in non-default display. - updateDisplay(displayId); - } - - /** - * {@inheritDoc} * * <p>Calls {@link InputMethodService#onBindInput()} when done.</p> */ diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 2ed0bad69460..308e6d56b96e 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -189,13 +189,11 @@ public class Environment { } @UnsupportedAppUsage - @Deprecated public File getExternalStorageDirectory() { return getExternalDirs()[0]; } @UnsupportedAppUsage - @Deprecated public File getExternalStoragePublicDirectory(String type) { return buildExternalStoragePublicDirs(type)[0]; } @@ -698,11 +696,7 @@ public class Environment { * * @see #getExternalStorageState() * @see #isExternalStorageRemovable() - * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)}, - * {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better - * performance. */ - @Deprecated public static File getExternalStorageDirectory() { throwIfUserRequired(); return sCurrentUser.getExternalDirs()[0]; @@ -1009,11 +1003,7 @@ public class Environment { * @return Returns the File path for the directory. Note that this directory * may not yet exist, so you must make sure it exists before using * it such as with {@link File#mkdirs File.mkdirs()}. - * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)}, - * {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better - * performance. */ - @Deprecated public static File getExternalStoragePublicDirectory(String type) { throwIfUserRequired(); return sCurrentUser.buildExternalStoragePublicDirs(type)[0]; diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 431bf4c54b4b..e7e53b39982a 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -597,6 +597,14 @@ public final class DeviceConfig { @TestApi public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis"; + /** + * Namespace for App Compat Overrides related features. + * + * @hide + */ + @TestApi + public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 802163617b3b..305cb97dbc77 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -865,4 +865,11 @@ interface IWindowManager void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener); boolean isTaskSnapshotSupported(); + + /** + * Returns the preferred display ID to show software keyboard. + * + * @see android.window.WindowProviderService#getLaunchedDisplayId + */ + int getImeDisplayId(); } diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 5a34a92a4b1a..1cb1439f4032 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -20,8 +20,8 @@ import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; import android.graphics.Region; +import android.gui.TouchOcclusionMode; import android.os.IBinder; -import android.os.TouchOcclusionMode; import java.lang.ref.WeakReference; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 69ff64f3d6a5..40942ea7f551 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -4008,6 +4008,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { public float orientation; /** + * The movement of x position of a motion event. + * + * @see MotionEvent#AXIS_RELATIVE_X + * @hide + */ + public float relativeX; + + /** + * The movement of y position of a motion event. + * + * @see MotionEvent#AXIS_RELATIVE_Y + * @hide + */ + public float relativeY; + + /** * Clears the contents of this object. * Resets all axes to zero. */ @@ -4023,6 +4039,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { toolMajor = 0; toolMinor = 0; orientation = 0; + relativeX = 0; + relativeY = 0; } /** @@ -4053,6 +4071,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { toolMajor = other.toolMajor; toolMinor = other.toolMinor; orientation = other.orientation; + relativeX = other.relativeX; + relativeY = other.relativeY; } /** @@ -4084,6 +4104,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { return toolMinor; case AXIS_ORIENTATION: return orientation; + case AXIS_RELATIVE_X: + return relativeX; + case AXIS_RELATIVE_Y: + return relativeY; default: { if (axis < 0 || axis > 63) { throw new IllegalArgumentException("Axis out of range."); @@ -4137,6 +4161,12 @@ public final class MotionEvent extends InputEvent implements Parcelable { case AXIS_ORIENTATION: orientation = value; break; + case AXIS_RELATIVE_X: + relativeX = value; + break; + case AXIS_RELATIVE_Y: + relativeY = value; + break; default: { if (axis < 0 || axis > 63) { throw new IllegalArgumentException("Axis out of range."); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index c03db6d67356..1d8eb5eeb104 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -162,6 +162,8 @@ public final class SurfaceControl implements Parcelable { IBinder displayToken, long nativeSurfaceObject); private static native void nativeSetDisplayLayerStack(long transactionObj, IBinder displayToken, int layerStack); + private static native void nativeSetDisplayFlags(long transactionObj, + IBinder displayToken, int flags); private static native void nativeSetDisplayProjection(long transactionObj, IBinder displayToken, int orientation, int l, int t, int r, int b, @@ -547,6 +549,15 @@ public final class SurfaceControl implements Parcelable { */ private static final int SURFACE_OPAQUE = 0x02; + /* flags used with setDisplayFlags() (keep in sync with DisplayDevice.h) */ + + /** + * DisplayDevice flag: This display's transform is sent to inputflinger and used for input + * dispatch. This flag is used to disambiguate displays which share a layerstack. + * @hide + */ + public static final int DISPLAY_RECEIVES_INPUT = 0x01; + // Display power modes. /** * Display power mode off: used while blanking the screen. @@ -3169,6 +3180,17 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public Transaction setDisplayFlags(IBinder displayToken, int flags) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeSetDisplayFlags(mNativeObject, displayToken, flags); + return this; + } + + /** + * @hide + */ public Transaction setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect) { if (displayToken == null) { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 4f2cf6d9001e..f04530f188e2 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1559,12 +1559,21 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * @hide */ public void setResizeBackgroundColor(int bgColor) { + setResizeBackgroundColor(mTmpTransaction, bgColor); + mTmpTransaction.apply(); + } + + /** + * Version of {@link #setResizeBackgroundColor(int)} that allows you to provide + * {@link SurfaceControl.Transaction}. + * @hide + */ + public void setResizeBackgroundColor(@NonNull SurfaceControl.Transaction t, int bgColor) { if (mBackgroundControl == null) { return; } - mBackgroundColor = bgColor; - updateBackgroundColor(mTmpTransaction).apply(); + updateBackgroundColor(t); } @UnsupportedAppUsage diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 23faac6c0787..155bc0a429be 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -284,6 +284,21 @@ public final class ViewRootImpl implements ViewParent, */ private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500; + /** + * If set to {@code true}, the new logic to layout system bars as normal window and to use + * layout result to get insets will be applied. Otherwise, the old hard-coded window logic will + * be applied. + */ + private static final String USE_FLEXIBLE_INSETS = "persist.debug.flexible_insets"; + + /** + * A flag to indicate to use the new generalized insets window logic, or the old hard-coded + * insets window layout logic. + * {@hide} + */ + public static final boolean INSETS_LAYOUT_GENERALIZATION = + SystemProperties.getBoolean(USE_FLEXIBLE_INSETS, false); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 55beae0f7b3d..fcfb0ab1a744 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -97,6 +97,7 @@ import android.content.ClipData; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -394,6 +395,11 @@ public interface WindowManager extends ViewManager { */ int TRANSIT_KEYGUARD_UNOCCLUDE = 9; /** + * A window is starting to enter PiP. + * @hide + */ + int TRANSIT_PIP = 10; + /** * The first slot for custom transition types. Callers (like Shell) can make use of custom * transition types for dealing with special cases. These types are effectively ignored by * Core and will just be passed along as part of TransitionInfo objects. An example is @@ -402,7 +408,7 @@ public interface WindowManager extends ViewManager { * implementation. * @hide */ - int TRANSIT_FIRST_CUSTOM = 10; + int TRANSIT_FIRST_CUSTOM = 11; /** * @hide @@ -418,6 +424,7 @@ public interface WindowManager extends ViewManager { TRANSIT_KEYGUARD_GOING_AWAY, TRANSIT_KEYGUARD_OCCLUDE, TRANSIT_KEYGUARD_UNOCCLUDE, + TRANSIT_PIP, TRANSIT_FIRST_CUSTOM }) @Retention(RetentionPolicy.SOURCE) @@ -467,6 +474,13 @@ public interface WindowManager extends ViewManager { int TRANSIT_FLAG_KEYGUARD_LOCKED = 0x40; /** + * Transition flag: Indicates that this transition is for recents animation. + * TODO(b/188669821): Remove once special-case logic moves to shell. + * @hide + */ + int TRANSIT_FLAG_IS_RECENTS = 0x80; + + /** * @hide */ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { @@ -476,7 +490,8 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION, TRANSIT_FLAG_APP_CRASHED, TRANSIT_FLAG_OPEN_BEHIND, - TRANSIT_FLAG_KEYGUARD_LOCKED + TRANSIT_FLAG_KEYGUARD_LOCKED, + TRANSIT_FLAG_IS_RECENTS }) @Retention(RetentionPolicy.SOURCE) @interface TransitionFlags {} @@ -3468,6 +3483,22 @@ public interface WindowManager extends ViewManager { public @InsetsState.InternalInsetsType int[] providesInsetsTypes; /** + * If specified, the insets provided by this window will be our window frame minus the + * insets specified by providedInternalInsets. + * + * @hide + */ + public Insets providedInternalInsets = Insets.NONE; + + /** + * {@link LayoutParams} to be applied to the window when layout with a assigned rotation. + * This will make layout during rotation change smoothly. + * + * @hide + */ + public LayoutParams[] paramsForRotation; + + /** * Specifies types of insets that this window should avoid overlapping during layout. * * @param types which {@link WindowInsets.Type}s of insets that this window should avoid. @@ -3566,6 +3597,18 @@ public interface WindowManager extends ViewManager { return mFitInsetsIgnoringVisibility; } + private void checkNonRecursiveParams() { + if (paramsForRotation == null) { + return; + } + for (int i = paramsForRotation.length - 1; i >= 0; i--) { + if (paramsForRotation[i].paramsForRotation != null) { + throw new IllegalArgumentException( + "Params cannot contain params recursively."); + } + } + } + public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; @@ -3820,6 +3863,14 @@ public interface WindowManager extends ViewManager { } else { out.writeInt(0); } + providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */); + if (paramsForRotation != null) { + checkNonRecursiveParams(); + out.writeInt(paramsForRotation.length); + out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } } public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR @@ -3891,6 +3942,12 @@ public interface WindowManager extends ViewManager { providesInsetsTypes = new int[insetsTypesLength]; in.readIntArray(providesInsetsTypes); } + providedInternalInsets = Insets.CREATOR.createFromParcel(in); + int paramsForRotationLength = in.readInt(); + if (paramsForRotationLength > 0) { + paramsForRotation = new LayoutParams[paramsForRotationLength]; + in.readTypedArray(paramsForRotation, LayoutParams.CREATOR); + } } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -4187,6 +4244,17 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } + if (!providedInternalInsets.equals(o.providedInternalInsets)) { + providedInternalInsets = o.providedInternalInsets; + changes |= LAYOUT_CHANGED; + } + + if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) { + paramsForRotation = o.paramsForRotation; + checkNonRecursiveParams(); + changes |= LAYOUT_CHANGED; + } + return changes; } @@ -4382,6 +4450,18 @@ public interface WindowManager extends ViewManager { sb.append(InsetsState.typeToString(providesInsetsTypes[i])); } } + if (!providedInternalInsets.equals(Insets.NONE)) { + sb.append(" providedInternalInsets="); + sb.append(providedInternalInsets); + } + if (paramsForRotation != null && paramsForRotation.length != 0) { + sb.append(System.lineSeparator()); + sb.append(prefix).append(" paramsForRotation="); + for (int i = 0; i < paramsForRotation.length; ++i) { + if (i > 0) sb.append(' '); + sb.append(paramsForRotation[i].toString()); + } + } sb.append('}'); return sb.toString(); diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index dd81dd93380b..aac09b8a3b57 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -16,6 +16,9 @@ package android.view.accessibility; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK; + import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; import android.annotation.Nullable; @@ -86,6 +89,7 @@ public final class AccessibilityInteractionClient public static final int NO_ID = -1; public static final String CALL_STACK = "call_stack"; + public static final String IGNORE_CALL_STACK = "ignore_call_stack"; private static final String LOG_TAG = "AccessibilityInteractionClient"; @@ -121,6 +125,12 @@ public final class AccessibilityInteractionClient private volatile int mInteractionId = -1; private volatile int mCallingUid = Process.INVALID_UID; + // call stack for IAccessibilityInteractionConnectionCallback APIs. These callback APIs are + // shared by multiple requests APIs in IAccessibilityServiceConnection. To correctly log the + // request API which triggers the callback, we log trace entries for callback after the + // request API thread waiting for the callback returns. To log the correct callback stack in + // the request API thread, we save the callback stack in this member variables. + private List<StackTraceElement> mCallStackOfCallback; private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult; @@ -307,18 +317,30 @@ public final class AccessibilityInteractionClient if (DEBUG) { Log.i(LOG_TAG, "Window cache hit"); } + if (shouldTraceClient()) { + logTraceClient(connection, "getWindow cache", + "connectionId=" + connectionId + ";accessibilityWindowId=" + + accessibilityWindowId + ";bypassCache=false"); + } return window; } if (DEBUG) { Log.i(LOG_TAG, "Window cache miss"); } } + final long identityToken = Binder.clearCallingIdentity(); try { window = connection.getWindow(accessibilityWindowId); } finally { Binder.restoreCallingIdentity(identityToken); } + if (shouldTraceClient()) { + logTraceClient(connection, "getWindow", "connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + ";bypassCache=" + + bypassCache); + } + if (window != null) { if (!bypassCache) { sAccessibilityCache.addWindow(window); @@ -368,6 +390,10 @@ public final class AccessibilityInteractionClient if (DEBUG) { Log.i(LOG_TAG, "Windows cache hit"); } + if (shouldTraceClient()) { + logTraceClient( + connection, "getWindows cache", "connectionId=" + connectionId); + } return windows; } if (DEBUG) { @@ -379,6 +405,9 @@ public final class AccessibilityInteractionClient } finally { Binder.restoreCallingIdentity(identityToken); } + if (shouldTraceClient()) { + logTraceClient(connection, "getWindows", "connectionId=" + connectionId); + } if (windows != null) { sAccessibilityCache.setWindowsOnAllDisplays(windows); return windows; @@ -472,6 +501,15 @@ public final class AccessibilityInteractionClient Log.i(LOG_TAG, "Node cache hit for " + idToString(accessibilityWindowId, accessibilityNodeId)); } + if (shouldTraceClient()) { + logTraceClient(connection, + "findAccessibilityNodeInfoByAccessibilityId cache", + "connectionId=" + connectionId + ";accessibilityWindowId=" + + accessibilityWindowId + ";accessibilityNodeId=" + + accessibilityNodeId + ";bypassCache=" + bypassCache + + ";prefetchFlags=" + prefetchFlags + ";arguments=" + + arguments); + } return cachedInfo; } if (DEBUG) { @@ -488,6 +526,14 @@ public final class AccessibilityInteractionClient prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK; } final int interactionId = mInteractionIdCounter.getAndIncrement(); + if (shouldTraceClient()) { + logTraceClient(connection, "findAccessibilityNodeInfoByAccessibilityId", + "InteractionId:" + interactionId + "connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";bypassCache=" + + bypassCache + ";prefetchFlags=" + prefetchFlags + ";arguments=" + + arguments); + } final String[] packageNames; final long identityToken = Binder.clearCallingIdentity(); try { @@ -500,16 +546,10 @@ public final class AccessibilityInteractionClient if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(interactionId); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { - logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId", - "InteractionId:" + interactionId + ";Result: " + info - + ";connectionId=" + connectionId - + ";accessibilityWindowId=" - + accessibilityWindowId + ";accessibilityNodeId=" - + accessibilityNodeId + ";bypassCache=" + bypassCache - + ";prefetchFlags=" + prefetchFlags - + ";arguments=" + arguments); + if (shouldTraceCallback()) { + logTraceCallback(connection, "findAccessibilityNodeInfoByAccessibilityId", + "InteractionId:" + interactionId + ";connectionId=" + + connectionId + ";Result: " + info); } if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0 && info != null) { @@ -571,6 +611,14 @@ public final class AccessibilityInteractionClient final String[] packageNames; final long identityToken = Binder.clearCallingIdentity(); try { + if (shouldTraceClient()) { + logTraceClient(connection, "findAccessibilityNodeInfosByViewId", + "InteractionId=" + interactionId + ";connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId=" + + viewId); + } + packageNames = connection.findAccessibilityNodeInfosByViewId( accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this, Thread.currentThread().getId()); @@ -581,13 +629,10 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { - logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId=" - + interactionId + ":Result: " + infos + ";connectionId=" - + connectionId + ";accessibilityWindowId=" + accessibilityWindowId - + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId=" - + viewId); + if (shouldTraceCallback()) { + logTraceCallback(connection, "findAccessibilityNodeInfosByViewId", + "InteractionId=" + interactionId + ";connectionId=" + connectionId + + ":Result: " + infos); } if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, @@ -630,6 +675,12 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); + if (shouldTraceClient()) { + logTraceClient(connection, "findAccessibilityNodeInfosByText", + "InteractionId:" + interactionId + "connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text); + } final String[] packageNames; final long identityToken = Binder.clearCallingIdentity(); try { @@ -643,12 +694,10 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { - logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId=" - + interactionId + ":Result: " + infos + ";connectionId=" - + connectionId + ";accessibilityWindowId=" + accessibilityWindowId - + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text); + if (shouldTraceCallback()) { + logTraceCallback(connection, "findAccessibilityNodeInfosByText", + "InteractionId=" + interactionId + ";connectionId=" + connectionId + + ";Result: " + infos); } if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, @@ -690,6 +739,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); + if (shouldTraceClient()) { + logTraceClient(connection, "findFocus", + "InteractionId:" + interactionId + "connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType=" + + focusType); + } final String[] packageNames; final long identityToken = Binder.clearCallingIdentity(); try { @@ -703,13 +759,9 @@ public final class AccessibilityInteractionClient if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { - logTrace(connection, "findFocus", "InteractionId=" + interactionId - + ":Result: " + info + ";connectionId=" + connectionId - + ";accessibilityWindowId=" + accessibilityWindowId - + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType=" - + focusType); + if (shouldTraceCallback()) { + logTraceCallback(connection, "findFocus", "InteractionId=" + interactionId + + ";connectionId=" + connectionId + ";Result:" + info); } finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; @@ -747,6 +799,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); + if (shouldTraceClient()) { + logTraceClient(connection, "focusSearch", + "InteractionId:" + interactionId + "connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";direction=" + + direction); + } final String[] packageNames; final long identityToken = Binder.clearCallingIdentity(); try { @@ -761,13 +820,9 @@ public final class AccessibilityInteractionClient AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { - logTrace(connection, "focusSearch", "InteractionId=" + interactionId - + ":Result: " + info + ";connectionId=" + connectionId - + ";accessibilityWindowId=" + accessibilityWindowId - + ";accessibilityNodeId=" + accessibilityNodeId + ";direction=" - + direction); + if (shouldTraceCallback()) { + logTraceCallback(connection, "focusSearch", "InteractionId=" + interactionId + + ";connectionId=" + connectionId + ";Result:" + info); } return info; } @@ -803,6 +858,13 @@ public final class AccessibilityInteractionClient IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { final int interactionId = mInteractionIdCounter.getAndIncrement(); + if (shouldTraceClient()) { + logTraceClient(connection, "performAccessibilityAction", + "InteractionId:" + interactionId + "connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + action + + ";arguments=" + arguments); + } final boolean success; final long identityToken = Binder.clearCallingIdentity(); try { @@ -816,13 +878,10 @@ public final class AccessibilityInteractionClient if (success) { final boolean result = getPerformAccessibilityActionResultAndClear(interactionId); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { - logTrace(connection, "performAccessibilityAction", "InteractionId=" - + interactionId + ":Result: " + result + ";connectionId=" - + connectionId + ";accessibilityWindowId=" + accessibilityWindowId - + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" - + action + ";arguments=" + arguments); + if (shouldTraceCallback()) { + logTraceCallback(connection, "performAccessibilityAction", + "InteractionId=" + interactionId + ";connectionId=" + connectionId + + ";Result: " + result); } return result; } @@ -886,6 +945,8 @@ public final class AccessibilityInteractionClient mFindAccessibilityNodeInfoResult = info; mInteractionId = interactionId; mCallingUid = Binder.getCallingUid(); + mCallStackOfCallback = new ArrayList<StackTraceElement>( + Arrays.asList(Thread.currentThread().getStackTrace())); } mInstanceLock.notifyAll(); } @@ -936,6 +997,8 @@ public final class AccessibilityInteractionClient } mInteractionId = interactionId; mCallingUid = Binder.getCallingUid(); + mCallStackOfCallback = new ArrayList<StackTraceElement>( + Arrays.asList(Thread.currentThread().getStackTrace())); } mInstanceLock.notifyAll(); } @@ -975,13 +1038,15 @@ public final class AccessibilityInteractionClient finalizeAndCacheAccessibilityNodeInfos( infos, connectionIdWaitingForPrefetchResultCopy, false, packageNamesForNextPrefetchResultCopy); - if (mAccessibilityManager != null - && mAccessibilityManager.isAccessibilityTracingEnabled()) { + if (shouldTraceCallback()) { logTrace(getConnection(connectionIdWaitingForPrefetchResultCopy), "setPrefetchAccessibilityNodeInfoResult", - "InteractionId:" + interactionId + ";Result: " + infos - + ";connectionId=" + connectionIdWaitingForPrefetchResultCopy, - Binder.getCallingUid()); + "InteractionId:" + interactionId + ";connectionId=" + + connectionIdWaitingForPrefetchResultCopy + ";Result: " + infos, + Binder.getCallingUid(), + Arrays.asList(Thread.currentThread().getStackTrace()), + new HashSet<String>(Arrays.asList("getStackTrace")), + FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK); } } else if (DEBUG) { Log.w(LOG_TAG, "Prefetching for interaction with id " + interactionId + " dropped " @@ -1013,6 +1078,8 @@ public final class AccessibilityInteractionClient mPerformAccessibilityActionResult = succeeded; mInteractionId = interactionId; mCallingUid = Binder.getCallingUid(); + mCallStackOfCallback = new ArrayList<StackTraceElement>( + Arrays.asList(Thread.currentThread().getStackTrace())); } mInstanceLock.notifyAll(); } @@ -1222,24 +1289,45 @@ public final class AccessibilityInteractionClient return true; } + private boolean shouldTraceClient() { + return (mAccessibilityManager != null) + && mAccessibilityManager.isA11yInteractionClientTraceEnabled(); + } + + private boolean shouldTraceCallback() { + return (mAccessibilityManager != null) + && mAccessibilityManager.isA11yInteractionConnectionCBTraceEnabled(); + } + private void logTrace( IAccessibilityServiceConnection connection, String method, String params, - int callingUid) { + int callingUid, List<StackTraceElement> callStack, HashSet<String> ignoreSet, + long logTypes) { try { Bundle b = new Bundle(); - ArrayList<StackTraceElement> callStack = new ArrayList<StackTraceElement>( - Arrays.asList(Thread.currentThread().getStackTrace())); - b.putSerializable(CALL_STACK, callStack); + b.putSerializable(CALL_STACK, new ArrayList<StackTraceElement>(callStack)); + if (ignoreSet != null) { + b.putSerializable(IGNORE_CALL_STACK, ignoreSet); + } connection.logTrace(SystemClock.elapsedRealtimeNanos(), - LOG_TAG + ".callback for " + method, params, Process.myPid(), - Thread.currentThread().getId(), callingUid, b); + LOG_TAG + "." + method, + logTypes, params, Process.myPid(), Thread.currentThread().getId(), + callingUid, b); } catch (RemoteException e) { Log.e(LOG_TAG, "Failed to log trace. " + e); } } - private void logTrace( + private void logTraceCallback( + IAccessibilityServiceConnection connection, String method, String params) { + logTrace(connection, method + " callback", params, mCallingUid, mCallStackOfCallback, + new HashSet<String>(Arrays.asList("getStackTrace")), + FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK); + } + + private void logTraceClient( IAccessibilityServiceConnection connection, String method, String params) { - logTrace(connection, method, params, mCallingUid); + logTrace(connection, method, params, Binder.getCallingUid(), + Collections.emptyList(), null, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT); } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index f9cdbd322c26..17fad7e57de7 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -111,7 +111,13 @@ public final class AccessibilityManager { public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010; /** @hide */ - public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020; + public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 0x00000100; + /** @hide */ + public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 0x00000200; + /** @hide */ + public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400; + /** @hide */ + public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800; /** @hide */ public static final int DALTONIZER_DISABLED = -1; @@ -235,8 +241,8 @@ public final class AccessibilityManager { @UnsupportedAppUsage(trackingBug = 123768939L) boolean mIsHighTextContrastEnabled; - // Whether accessibility tracing is enabled or not - boolean mIsAccessibilityTracingEnabled = false; + // accessibility tracing state + int mAccessibilityTracingState = 0; AccessibilityPolicy mAccessibilityPolicy; @@ -1029,13 +1035,50 @@ public final class AccessibilityManager { } /** - * Gets accessibility tracing enabled state. + * Gets accessibility interaction connection tracing enabled state. + * + * @hide + */ + public boolean isA11yInteractionConnectionTraceEnabled() { + synchronized (mLock) { + return ((mAccessibilityTracingState + & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0); + } + } + + /** + * Gets accessibility interaction connection callback tracing enabled state. + * + * @hide + */ + public boolean isA11yInteractionConnectionCBTraceEnabled() { + synchronized (mLock) { + return ((mAccessibilityTracingState + & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0); + } + } + + /** + * Gets accessibility interaction client tracing enabled state. + * + * @hide + */ + public boolean isA11yInteractionClientTraceEnabled() { + synchronized (mLock) { + return ((mAccessibilityTracingState + & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0); + } + } + + /** + * Gets accessibility service tracing enabled state. * * @hide */ - public boolean isAccessibilityTracingEnabled() { + public boolean isA11yServiceTraceEnabled() { synchronized (mLock) { - return mIsAccessibilityTracingEnabled; + return ((mAccessibilityTracingState + & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0); } } @@ -1233,8 +1276,6 @@ public final class AccessibilityManager { (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; final boolean highTextContrastEnabled = (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0; - final boolean accessibilityTracingEnabled = - (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0; final boolean wasEnabled = isEnabled(); final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; @@ -1257,7 +1298,7 @@ public final class AccessibilityManager { notifyHighTextContrastStateChanged(); } - updateAccessibilityTracingState(accessibilityTracingEnabled); + updateAccessibilityTracingState(stateFlags); } /** @@ -1715,11 +1756,11 @@ public final class AccessibilityManager { } /** - * Update mIsAccessibilityTracingEnabled. + * Update mAccessibilityTracingState. */ - private void updateAccessibilityTracingState(boolean enabled) { + private void updateAccessibilityTracingState(int stateFlag) { synchronized (mLock) { - mIsAccessibilityTracingEnabled = enabled; + mAccessibilityTracingState = stateFlag; } } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index d2db0df6c597..5b2068ff16cd 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -96,8 +96,6 @@ public interface InputMethod { * * @param token special token for the system to identify * {@link InputMethodService} - * @param displayId The id of the display that current IME shown. - * Used for {{@link #updateInputMethodDisplay(int)}} * @param privilegedOperations IPC endpoint to do some privileged * operations that are allowed only to the * current IME. @@ -105,9 +103,8 @@ public interface InputMethod { * @hide */ @MainThread - default void initializeInternal(IBinder token, int displayId, + default void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { - updateInputMethodDisplay(displayId); attachToken(token); } @@ -143,16 +140,6 @@ public interface InputMethod { public void attachToken(IBinder token); /** - * Update context display according to given displayId. - * - * @param displayId The id of the display that need to update for context. - * @hide - */ - @MainThread - default void updateInputMethodDisplay(int displayId) { - } - - /** * Bind a new application environment in to the input method, so that it * can later start and stop input processing. * Typically this method is called when this input method is enabled in an diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java new file mode 100644 index 000000000000..9a079751553f --- /dev/null +++ b/core/java/android/window/ConfigurationHelper.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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 android.window; + +import static android.view.Display.INVALID_DISPLAY; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ResourcesManager; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.os.IBinder; +import android.view.Display; +import android.view.WindowManager; + +/** + * A helper class to maintain {@link android.content.res.Configuration} related methods used both + * in {@link android.app.Activity} and {@link WindowContext}. + * + * @hide + */ +public class ConfigurationHelper { + private ConfigurationHelper() {} + + /** Ask text layout engine to free its caches if there is a locale change. */ + public static void freeTextLayoutCachesIfNeeded(int configDiff) { + if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) { + Canvas.freeTextLayoutCaches(); + } + } + + /** + * A helper method to filter out {@link ActivityInfo#CONFIG_SCREEN_SIZE} if the + * {@link Configuration#diffPublicOnly(Configuration) diff} of two {@link Configuration} + * doesn't cross the boundary. + * + * @see SizeConfigurationBuckets#filterDiff(int, Configuration, Configuration, + * SizeConfigurationBuckets) + */ + public static int diffPublicWithSizeBuckets(@Nullable Configuration currentConfig, + @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) { + // If current configuration is null, it is definitely different from updated Configuration. + if (currentConfig == null) { + return 0xffffffff; + } + int publicDiff = currentConfig.diffPublicOnly(newConfig); + return SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig, newConfig, buckets); + } + + /** + * Returns {@code true} if the {@link android.content.res.Resources} associated with + * a {@code token} needs to be updated. + * + * @param token A {@link Context#getActivityToken() activity token} or + * {@link Context#getWindowContextToken() window context token} + * @param config The original {@link Configuration} + * @param newConfig The updated Configuration + * @param displayChanged a flag to indicate there's a display change + * @param configChanged a flag to indicate there's a Configuration change. + * + * @see ResourcesManager#updateResourcesForActivity(IBinder, Configuration, int) + */ + public static boolean shouldUpdateResources(IBinder token, @Nullable Configuration config, + @NonNull Configuration newConfig, @NonNull Configuration overrideConfig, + boolean displayChanged, @Nullable Boolean configChanged) { + // The configuration has not yet been initialized. We should update it. + if (config == null) { + return true; + } + // If the token associated context is moved to another display, we should update the + // ResourcesKey. + if (displayChanged) { + return true; + } + // If the new config is the same as the config this Activity is already running with and + // the override config also didn't change, then don't update the Resources + if (!ResourcesManager.getInstance().isSameResourcesOverrideConfig(token, overrideConfig)) { + return true; + } + // If there's a update on WindowConfiguration#mBounds or maxBounds, we should update the + // Resources to make WindowMetrics API report the updated result. + if (shouldUpdateWindowMetricsBounds(config, newConfig)) { + return true; + } + return configChanged == null ? config.diff(newConfig) != 0 : configChanged; + } + + /** + * Returns {@code true} if {@code displayId} is different from {@code newDisplayId}. + * Note that {@link Display#INVALID_DISPLAY} means no difference. + */ + public static boolean isDifferentDisplay(int displayId, int newDisplayId) { + return newDisplayId != INVALID_DISPLAY && displayId != newDisplayId; + } + + // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl + // constructions. + /** + * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs + * should be updated. + * + * @see WindowManager#getCurrentWindowMetrics() + * @see WindowManager#getMaximumWindowMetrics() + */ + private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig, + @NonNull Configuration newConfig) { + final Rect currentBounds = currentConfig.windowConfiguration.getBounds(); + final Rect newBounds = newConfig.windowConfiguration.getBounds(); + + final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds(); + final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds(); + + return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds); + } +} diff --git a/core/java/android/window/DisplayAreaInfo.java b/core/java/android/window/DisplayAreaInfo.java index 358467ff599f..1a7aab6852b6 100644 --- a/core/java/android/window/DisplayAreaInfo.java +++ b/core/java/android/window/DisplayAreaInfo.java @@ -16,6 +16,8 @@ package android.window; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; + import android.annotation.NonNull; import android.annotation.TestApi; import android.content.res.Configuration; @@ -43,8 +45,17 @@ public final class DisplayAreaInfo implements Parcelable { */ public final int displayId; + /** + * The feature id of this display area. + */ public final int featureId; + /** + * The feature id of the root display area this display area is associated with. + * @hide + */ + public int rootDisplayAreaId = FEATURE_UNDEFINED; + public DisplayAreaInfo(@NonNull WindowContainerToken token, int displayId, int featureId) { this.token = token; this.displayId = displayId; @@ -56,6 +67,7 @@ public final class DisplayAreaInfo implements Parcelable { configuration.readFromParcel(in); displayId = in.readInt(); featureId = in.readInt(); + rootDisplayAreaId = in.readInt(); } @Override @@ -64,6 +76,7 @@ public final class DisplayAreaInfo implements Parcelable { configuration.writeToParcel(dest, flags); dest.writeInt(displayId); dest.writeInt(featureId); + dest.writeInt(rootDisplayAreaId); } @NonNull diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 878439906de2..e6746556fb67 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -34,6 +34,15 @@ import java.util.concurrent.Executor; public class DisplayAreaOrganizer extends WindowOrganizer { /** + * Key to specify the {@link com.android.server.wm.RootDisplayArea} to attach a window to. + * It will be used by the function passed in from + * {@link com.android.server.wm.DisplayAreaPolicyBuilder#setSelectRootForWindowFunc(BiFunction)} + * to find the Root DA to attach the window. + * @hide + */ + public static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id"; + + /** * The value in display area indicating that no value has been set. */ public static final int FEATURE_UNDEFINED = -1; diff --git a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl index 02aa1a93a35f..7864c245310e 100644 --- a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl +++ b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl @@ -16,14 +16,18 @@ package android.window; +import android.view.SurfaceControl; import android.window.WindowContainerTransaction; /** * Interface to be invoked by the controlling process when a remote transition has finished. * * @see IRemoteTransition + * @param wct An optional WindowContainerTransaction to apply before the transition finished. + * @param sct An optional Surface Transaction that is added to the end of the finish/cleanup + * transaction. This is applied by shell.Transitions (before submitting the wct). * {@hide} */ interface IRemoteTransitionFinishedCallback { - void onTransitionFinished(in WindowContainerTransaction wct); + void onTransitionFinished(in WindowContainerTransaction wct, in SurfaceControl.Transaction sct); } diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl new file mode 100644 index 000000000000..0bfc2541061c --- /dev/null +++ b/core/java/android/window/ITaskFragmentOrganizer.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2021 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 android.window; + +import android.content.res.Configuration; +import android.os.IBinder; +import android.window.TaskFragmentAppearedInfo; +import android.window.TaskFragmentInfo; + +/** @hide */ +oneway interface ITaskFragmentOrganizer { + void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo); + void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo); + void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo); + + /** + * Called when the parent leaf Task of organized TaskFragments is changed. + * When the leaf Task is changed, the organizer may want to update the TaskFragments in one + * transaction. + * + * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new + * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override + * bounds. + */ + void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig); +} diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl new file mode 100644 index 000000000000..0ca8a864dba5 --- /dev/null +++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021 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 android.window; + +import android.window.ITaskFragmentOrganizer; + +/** @hide */ +interface ITaskFragmentOrganizerController { + + /** + * Registers a TaskFragmentOrganizer to manage TaskFragments. + */ + void registerOrganizer(in ITaskFragmentOrganizer organizer); + + /** + * Unregisters a previously registered TaskFragmentOrganizer. + */ + void unregisterOrganizer(in ITaskFragmentOrganizer organizer); +} diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 1223d72f643e..39cdf5af50ce 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -20,6 +20,7 @@ import android.view.SurfaceControl; import android.os.IBinder; import android.window.IDisplayAreaOrganizerController; +import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; @@ -77,6 +78,9 @@ interface IWindowOrganizerController { /** @return An interface enabling the management of display area organizers. */ IDisplayAreaOrganizerController getDisplayAreaOrganizerController(); + /** @return An interface enabling the management of task fragment organizers. */ + ITaskFragmentOrganizerController getTaskFragmentOrganizerController(); + /** * Registers a transition player with Core. There is only one of these at a time and calling * this will replace the existing one if set. diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/window/TaskFragmentAppearedInfo.aidl new file mode 100644 index 000000000000..3729c09168a6 --- /dev/null +++ b/core/java/android/window/TaskFragmentAppearedInfo.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 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 android.window; + +/** + * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer. + * @hide + */ +parcelable TaskFragmentAppearedInfo; diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java new file mode 100644 index 000000000000..234b30c0662c --- /dev/null +++ b/core/java/android/window/TaskFragmentAppearedInfo.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 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 android.window; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.SurfaceControl; + +/** + * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer. + * @hide + */ +public final class TaskFragmentAppearedInfo implements Parcelable { + + @NonNull + private final TaskFragmentInfo mTaskFragmentInfo; + + @NonNull + private final SurfaceControl mLeash; + + public TaskFragmentAppearedInfo( + @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) { + mTaskFragmentInfo = taskFragmentInfo; + mLeash = leash; + } + + public TaskFragmentInfo getTaskFragmentInfo() { + return mTaskFragmentInfo; + } + + public SurfaceControl getLeash() { + return mLeash; + } + + private TaskFragmentAppearedInfo(Parcel in) { + mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR); + mLeash = in.readTypedObject(SurfaceControl.CREATOR); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mTaskFragmentInfo, flags); + dest.writeTypedObject(mLeash, flags); + } + + @NonNull + public static final Creator<TaskFragmentAppearedInfo> CREATOR = + new Creator<TaskFragmentAppearedInfo>() { + @Override + public TaskFragmentAppearedInfo createFromParcel(Parcel in) { + return new TaskFragmentAppearedInfo(in); + } + + @Override + public TaskFragmentAppearedInfo[] newArray(int size) { + return new TaskFragmentAppearedInfo[size]; + } + }; + + @Override + public String toString() { + return "TaskFragmentAppearedInfo{" + + " taskFragmentInfo=" + mTaskFragmentInfo + + "}"; + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/window/TaskFragmentCreationParams.aidl b/core/java/android/window/TaskFragmentCreationParams.aidl new file mode 100644 index 000000000000..fde50892640b --- /dev/null +++ b/core/java/android/window/TaskFragmentCreationParams.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 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 android.window; + +/** + * Data object for options to create TaskFragment with. + * @hide + */ +parcelable TaskFragmentCreationParams; diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java new file mode 100644 index 000000000000..e4d6a6c07835 --- /dev/null +++ b/core/java/android/window/TaskFragmentCreationParams.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2021 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 android.window; + +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.WindowingMode; + +import android.annotation.NonNull; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Data object for options to create TaskFragment with. + * @hide + */ +public final class TaskFragmentCreationParams implements Parcelable { + + /** The organizer that will organize this TaskFragment. */ + @NonNull + private final ITaskFragmentOrganizer mOrganizer; + + /** + * Unique token assigned from the client organizer to identify the {@link TaskFragmentInfo} when + * a new TaskFragment is created with this option. + */ + @NonNull + private final IBinder mFragmentToken; + + /** + * Activity token used to identify the leaf Task to create the TaskFragment in. It has to belong + * to the same app as the root Activity of the target Task. + */ + @NonNull + private final IBinder mOwnerToken; + + /** The initial bounds of the TaskFragment. Fills parent if empty. */ + @NonNull + private final Rect mInitialBounds = new Rect(); + + /** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */ + @WindowingMode + private int mWindowingMode = WINDOWING_MODE_UNDEFINED; + + private TaskFragmentCreationParams( + @NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken, + @NonNull IBinder ownerToken) { + mOrganizer = organizer; + mFragmentToken = fragmentToken; + mOwnerToken = ownerToken; + } + + public ITaskFragmentOrganizer getOrganizer() { + return mOrganizer; + } + + public IBinder getFragmentToken() { + return mFragmentToken; + } + + public IBinder getOwnerToken() { + return mOwnerToken; + } + + public Rect getInitialBounds() { + return mInitialBounds; + } + + @WindowingMode + public int getWindowingMode() { + return mWindowingMode; + } + + private TaskFragmentCreationParams(Parcel in) { + mOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder()); + mFragmentToken = in.readStrongBinder(); + mOwnerToken = in.readStrongBinder(); + mInitialBounds.readFromParcel(in); + mWindowingMode = in.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongInterface(mOrganizer); + dest.writeStrongBinder(mFragmentToken); + dest.writeStrongBinder(mOwnerToken); + mInitialBounds.writeToParcel(dest, flags); + dest.writeInt(mWindowingMode); + } + + @NonNull + public static final Creator<TaskFragmentCreationParams> CREATOR = + new Creator<TaskFragmentCreationParams>() { + @Override + public TaskFragmentCreationParams createFromParcel(Parcel in) { + return new TaskFragmentCreationParams(in); + } + + @Override + public TaskFragmentCreationParams[] newArray(int size) { + return new TaskFragmentCreationParams[size]; + } + }; + + @Override + public String toString() { + return "TaskFragmentCreationParams{" + + " organizer=" + mOrganizer + + " fragmentToken=" + mFragmentToken + + " ownerToken=" + mOwnerToken + + " initialBounds=" + mInitialBounds + + " windowingMode=" + mWindowingMode + + "}"; + } + + @Override + public int describeContents() { + return 0; + } + + /** Builder to construct the options to create TaskFragment with. */ + public static class Builder { + + @NonNull + private final ITaskFragmentOrganizer mOrganizer; + + @NonNull + private final IBinder mFragmentToken; + + @NonNull + private final IBinder mOwnerToken; + + @NonNull + private final Rect mInitialBounds = new Rect(); + + @WindowingMode + private int mWindowingMode = WINDOWING_MODE_UNDEFINED; + + public Builder(@NonNull ITaskFragmentOrganizer organizer, @NonNull IBinder fragmentToken, + @NonNull IBinder ownerToken) { + mOrganizer = organizer; + mFragmentToken = fragmentToken; + mOwnerToken = ownerToken; + } + + /** Sets the initial bounds for the TaskFragment. */ + public Builder setInitialBounds(@NonNull Rect bounds) { + mInitialBounds.set(bounds); + return this; + } + + /** Sets the initial windowing mode for the TaskFragment. */ + public Builder setWindowingMode(@WindowingMode int windowingMode) { + mWindowingMode = windowingMode; + return this; + } + + /** Constructs the options to create TaskFragment with. */ + public TaskFragmentCreationParams build() { + final TaskFragmentCreationParams result = new TaskFragmentCreationParams( + mOrganizer, mFragmentToken, mOwnerToken); + result.mInitialBounds.set(mInitialBounds); + result.mWindowingMode = mWindowingMode; + return result; + } + } +} diff --git a/core/java/android/window/TaskFragmentInfo.aidl b/core/java/android/window/TaskFragmentInfo.aidl new file mode 100644 index 000000000000..461a7364803f --- /dev/null +++ b/core/java/android/window/TaskFragmentInfo.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 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 android.window; + +/** + * Stores information about a particular TaskFragment. + * @hide + */ +parcelable TaskFragmentInfo; diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java new file mode 100644 index 000000000000..e032153bc013 --- /dev/null +++ b/core/java/android/window/TaskFragmentInfo.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2021 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 android.window; + +import static android.app.WindowConfiguration.WindowingMode; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.res.Configuration; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Stores information about a particular TaskFragment. + * @hide + */ +public final class TaskFragmentInfo implements Parcelable { + + /** + * Client assigned unique token in {@link TaskFragmentCreationParams#fragmentToken} to create + * this TaskFragment with. + */ + @NonNull + private final IBinder mFragmentToken; + + /** + * The component name of the initial root activity of this TaskFragment, which will be used + * to configure the relationships for TaskFragments. + */ + @NonNull + private final ComponentName mInitialComponentName; + + @NonNull + private final WindowContainerToken mToken; + + @NonNull + private final Configuration mConfiguration = new Configuration(); + + /** Whether the TaskFragment contains any child Activity. */ + private final boolean mIsEmpty; + + /** Whether this TaskFragment is visible on the window hierarchy. */ + private final boolean mIsVisible; + + public TaskFragmentInfo( + @NonNull IBinder fragmentToken, @NonNull ComponentName initialComponentName, + @NonNull WindowContainerToken token, @NonNull Configuration configuration, + boolean isEmpty, boolean isVisible) { + if (fragmentToken == null || initialComponentName == null) { + throw new IllegalArgumentException("Invalid TaskFragmentInfo."); + } + mFragmentToken = fragmentToken; + mInitialComponentName = initialComponentName; + mToken = token; + mConfiguration.setTo(configuration); + mIsEmpty = isEmpty; + mIsVisible = isVisible; + } + + public IBinder getFragmentToken() { + return mFragmentToken; + } + + public ComponentName getInitialComponentName() { + return mInitialComponentName; + } + + public WindowContainerToken getToken() { + return mToken; + } + + public Configuration getConfiguration() { + return mConfiguration; + } + + public boolean isEmpty() { + return mIsEmpty; + } + + public boolean isVisible() { + return mIsVisible; + } + + @WindowingMode + public int getWindowingMode() { + return mConfiguration.windowConfiguration.getWindowingMode(); + } + + /** + * Returns {@code true} if the parameters that are important for task fragment organizers are + * equal between this {@link TaskFragmentInfo} and {@param that}. + */ + public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentInfo that) { + if (that == null) { + return false; + } + + return mFragmentToken.equals(that.mFragmentToken) + && mInitialComponentName.equals(that.mInitialComponentName) + && mToken.equals(that.mToken) + && mIsEmpty == that.mIsEmpty + && mIsVisible == that.mIsVisible + && getWindowingMode() == that.getWindowingMode(); + } + + private TaskFragmentInfo(Parcel in) { + mFragmentToken = in.readStrongBinder(); + mInitialComponentName = in.readTypedObject(ComponentName.CREATOR); + mToken = in.readTypedObject(WindowContainerToken.CREATOR); + mConfiguration.readFromParcel(in); + mIsEmpty = in.readBoolean(); + mIsVisible = in.readBoolean(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(mFragmentToken); + dest.writeTypedObject(mInitialComponentName, flags); + dest.writeTypedObject(mToken, flags); + mConfiguration.writeToParcel(dest, flags); + dest.writeBoolean(mIsEmpty); + dest.writeBoolean(mIsVisible); + } + + @NonNull + public static final Creator<TaskFragmentInfo> CREATOR = + new Creator<TaskFragmentInfo>() { + @Override + public TaskFragmentInfo createFromParcel(Parcel in) { + return new TaskFragmentInfo(in); + } + + @Override + public TaskFragmentInfo[] newArray(int size) { + return new TaskFragmentInfo[size]; + } + }; + + @Override + public String toString() { + return "TaskFragmentInfo{" + + " fragmentToken=" + mFragmentToken + + " initialComponentName=" + mInitialComponentName + + " token=" + mToken + + " isEmpty=" + mIsEmpty + + " isVisible=" + mIsVisible + + "}"; + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java new file mode 100644 index 000000000000..51722cccb44d --- /dev/null +++ b/core/java/android/window/TaskFragmentOrganizer.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2021 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 android.window; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.content.res.Configuration; +import android.os.IBinder; +import android.os.RemoteException; + +import java.util.concurrent.Executor; + +/** + * Interface for WindowManager to delegate control of {@link com.android.server.wm.TaskFragment}. + * @hide + */ +public class TaskFragmentOrganizer extends WindowOrganizer { + + /** + * Callbacks from WM Core are posted on this executor. + */ + private final Executor mExecutor; + + public TaskFragmentOrganizer(@NonNull Executor executor) { + mExecutor = executor; + } + + /** + * Gets the executor to run callbacks on. + */ + @NonNull + public Executor getExecutor() { + return mExecutor; + } + + /** + * Registers a TaskFragmentOrganizer to manage TaskFragments. + */ + @CallSuper + public void registerOrganizer() { + try { + getController().registerOrganizer(mInterface); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a previously registered TaskFragmentOrganizer. + */ + @CallSuper + public void unregisterOrganizer() { + try { + getController().unregisterOrganizer(mInterface); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** Called when a TaskFragment is created and organized by this organizer. */ + public void onTaskFragmentAppeared( + @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {} + + /** Called when the status of an organized TaskFragment is changed. */ + public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {} + + /** Called when an organized TaskFragment is removed. */ + public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {} + + /** + * Called when the parent leaf Task of organized TaskFragments is changed. + * When the leaf Task is changed, the organizer may want to update the TaskFragments in one + * transaction. + * + * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new + * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override + * bounds. + */ + public void onTaskFragmentParentInfoChanged( + @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {} + + private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() { + @Override + public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo)); + } + + @Override + public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo)); + } + + @Override + public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo)); + } + + @Override + public void onTaskFragmentParentInfoChanged( + @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) { + mExecutor.execute( + () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged( + fragmentToken, parentConfig)); + } + }; + + private ITaskFragmentOrganizerController getController() { + try { + return getWindowOrganizerController().getTaskFragmentOrganizerController(); + } catch (RemoteException e) { + return null; + } + } +} diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java index 141f47b130d1..6351b0395020 100644 --- a/core/java/android/window/TransitionFilter.java +++ b/core/java/android/window/TransitionFilter.java @@ -18,6 +18,7 @@ package android.window; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; @@ -33,6 +34,18 @@ import android.os.Parcelable; */ public final class TransitionFilter implements Parcelable { + /** The associated requirement doesn't care about the z-order. */ + public static final int CONTAINER_ORDER_ANY = 0; + /** The associated requirement only matches the top-most (z-order) container. */ + public static final int CONTAINER_ORDER_TOP = 1; + + /** @hide */ + @IntDef(prefix = { "CONTAINER_ORDER_" }, value = { + CONTAINER_ORDER_ANY, + CONTAINER_ORDER_TOP, + }) + public @interface ContainerOrder {} + /** * When non-null: this is a list of transition types that this filter applies to. This filter * will fail for transitions that aren't one of these types. @@ -126,6 +139,7 @@ public final class TransitionFilter implements Parcelable { public static final class Requirement implements Parcelable { public int mActivityType = ACTIVITY_TYPE_UNDEFINED; public int[] mModes = null; + public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY; public Requirement() { } @@ -133,6 +147,7 @@ public final class TransitionFilter implements Parcelable { private Requirement(Parcel in) { mActivityType = in.readInt(); mModes = in.createIntArray(); + mOrder = in.readInt(); } /** Go through changes and find if at-least one change matches this filter */ @@ -143,6 +158,9 @@ public final class TransitionFilter implements Parcelable { // Only look at independent animating windows. continue; } + if (mOrder == CONTAINER_ORDER_TOP && i > 0) { + continue; + } if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { if (change.getTaskInfo() == null || change.getTaskInfo().getActivityType() != mActivityType) { @@ -166,7 +184,7 @@ public final class TransitionFilter implements Parcelable { /** Check if the request matches this filter. It may generate false positives */ boolean matches(@NonNull TransitionRequestInfo request) { - // Can't check modes since the transition hasn't been built at this point. + // Can't check modes/order since the transition hasn't been built at this point. if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true; return request.getTriggerTask() != null && request.getTriggerTask().getActivityType() == mActivityType; @@ -177,6 +195,7 @@ public final class TransitionFilter implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mActivityType); dest.writeIntArray(mModes); + dest.writeInt(mOrder); } @NonNull @@ -209,7 +228,17 @@ public final class TransitionFilter implements Parcelable { out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i])); } } - return out.append("]}").toString(); + out.append("]").toString(); + out.append(" order=" + containerOrderToString(mOrder)); + return out.toString(); + } + } + + private static String containerOrderToString(int order) { + switch (order) { + case CONTAINER_ORDER_ANY: return "ANY"; + case CONTAINER_ORDER_TOP: return "TOP"; } + return "UNKNOWN(" + order + ")"; } } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 23b8ee4a019f..6430394804d2 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -16,6 +16,12 @@ package android.window; +import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; +import static android.app.ActivityOptions.ANIM_CUSTOM; +import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; +import static android.app.ActivityOptions.ANIM_SCALE_UP; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; @@ -31,6 +37,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.graphics.Point; import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.os.Parcel; import android.os.Parcelable; import android.view.Surface; @@ -102,6 +109,8 @@ public final class TransitionInfo implements Parcelable { private SurfaceControl mRootLeash; private final Point mRootOffset = new Point(); + private AnimationOptions mOptions; + /** @hide */ public TransitionInfo(@WindowManager.TransitionOldType int type, @WindowManager.TransitionFlags int flags) { @@ -116,6 +125,7 @@ public final class TransitionInfo implements Parcelable { mRootLeash = new SurfaceControl(); mRootLeash.readFromParcel(in); mRootOffset.readFromParcel(in); + mOptions = in.readTypedObject(AnimationOptions.CREATOR); } @Override @@ -126,6 +136,7 @@ public final class TransitionInfo implements Parcelable { dest.writeList(mChanges); mRootLeash.writeToParcel(dest, flags); mRootOffset.writeToParcel(dest, flags); + dest.writeTypedObject(mOptions, flags); } @NonNull @@ -154,6 +165,10 @@ public final class TransitionInfo implements Parcelable { mRootOffset.set(offsetLeft, offsetTop); } + public void setAnimationOptions(AnimationOptions options) { + mOptions = options; + } + public int getType() { return mType; } @@ -182,6 +197,10 @@ public final class TransitionInfo implements Parcelable { return mRootOffset; } + public AnimationOptions getAnimationOptions() { + return mOptions; + } + @NonNull public List<Change> getChanges() { return mChanges; @@ -484,4 +503,146 @@ public final class TransitionInfo implements Parcelable { + mStartRotation + "->" + mEndRotation + "}"; } } + + /** Represents animation options during a transition */ + public static final class AnimationOptions implements Parcelable { + + private int mType; + private int mEnterResId; + private int mExitResId; + private boolean mOverrideTaskTransition; + private String mPackageName; + private final Rect mTransitionBounds = new Rect(); + private HardwareBuffer mThumbnail; + + private AnimationOptions(int type) { + mType = type; + } + + public AnimationOptions(Parcel in) { + mType = in.readInt(); + mEnterResId = in.readInt(); + mExitResId = in.readInt(); + mOverrideTaskTransition = in.readBoolean(); + mPackageName = in.readString(); + mTransitionBounds.readFromParcel(in); + mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR); + } + + public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId, + int exitResId, boolean overrideTaskTransition) { + AnimationOptions options = new AnimationOptions(ANIM_CUSTOM); + options.mPackageName = packageName; + options.mEnterResId = enterResId; + options.mExitResId = exitResId; + options.mOverrideTaskTransition = overrideTaskTransition; + return options; + } + + public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width, + int height) { + AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL); + options.mTransitionBounds.set(startX, startY, startX + width, startY + height); + return options; + } + + public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width, + int height) { + AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP); + options.mTransitionBounds.set(startX, startY, startX + width, startY + height); + return options; + } + + public static AnimationOptions makeThumnbnailAnimOptions(HardwareBuffer srcThumb, + int startX, int startY, boolean scaleUp) { + AnimationOptions options = new AnimationOptions( + scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN); + options.mTransitionBounds.set(startX, startY, startX, startY); + options.mThumbnail = srcThumb; + return options; + } + + public static AnimationOptions makeCrossProfileAnimOptions() { + AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS); + return options; + } + + public int getType() { + return mType; + } + + public int getEnterResId() { + return mEnterResId; + } + + public int getExitResId() { + return mExitResId; + } + + public boolean getOverrideTaskTransition() { + return mOverrideTaskTransition; + } + + public String getPackageName() { + return mPackageName; + } + + public Rect getTransitionBounds() { + return mTransitionBounds; + } + + public HardwareBuffer getThumbnail() { + return mThumbnail; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mEnterResId); + dest.writeInt(mExitResId); + dest.writeBoolean(mOverrideTaskTransition); + dest.writeString(mPackageName); + mTransitionBounds.writeToParcel(dest, flags); + dest.writeTypedObject(mThumbnail, flags); + } + + @NonNull + public static final Creator<AnimationOptions> CREATOR = + new Creator<AnimationOptions>() { + @Override + public AnimationOptions createFromParcel(Parcel in) { + return new AnimationOptions(in); + } + + @Override + public AnimationOptions[] newArray(int size) { + return new AnimationOptions[size]; + } + }; + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + @NonNull + private static String typeToString(int mode) { + switch(mode) { + case ANIM_CUSTOM: return "ANIM_CUSTOM"; + case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL"; + case ANIM_SCALE_UP: return "ANIM_SCALE_UP"; + case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP"; + case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN"; + case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS"; + default: return "<unknown:" + mode + ">"; + } + } + + @Override + public String toString() { + return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName + + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}"; + } + } } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index c0af57214e5e..8c3dc2e2044e 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.app.WindowConfiguration; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; @@ -325,7 +326,8 @@ public final class WindowContainerTransaction implements Parcelable { /** * Sets to containers adjacent to each other. Containers below two visible adjacent roots will - * be made invisible. This currently only applies to Task containers created by organizer. + * be made invisible. This currently only applies to TaskFragment containers created by + * organizer. * @param root1 the first root. * @param root2 the second root. */ @@ -378,6 +380,102 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Creates a new TaskFragment with the given options. + * @param taskFragmentOptions the options used to create the TaskFragment. + * @hide + */ + @NonNull + public WindowContainerTransaction createTaskFragment( + @NonNull TaskFragmentCreationParams taskFragmentOptions) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT) + .setTaskFragmentCreationOptions(taskFragmentOptions) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed. + * @param taskFragment the TaskFragment to be removed. + * @hide + */ + @NonNull + public WindowContainerTransaction deleteTaskFragment( + @NonNull WindowContainerToken taskFragment) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT) + .setContainer(taskFragment.asBinder()) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Starts an activity in the TaskFragment. + * @param fragmentToken client assigned unique token to create TaskFragment with specified in + * {@link TaskFragmentCreationParams#fragmentToken}. + * @param activityIntent intent to start the activity. + * @param activityOptions ActivityOptions to start the activity with. + * @see android.content.Context#startActivity(Intent, Bundle). + * @hide + */ + @NonNull + public WindowContainerTransaction startActivityInTaskFragment( + @NonNull IBinder fragmentToken, @NonNull Intent activityIntent, + @Nullable Bundle activityOptions) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT) + .setContainer(fragmentToken) + .setActivityIntent(activityIntent) + .setLaunchOptions(activityOptions) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Moves an activity into the TaskFragment. + * @param fragmentToken client assigned unique token to create TaskFragment with specified in + * {@link TaskFragmentCreationParams#fragmentToken}. + * @param activityToken activity to be reparented. + * @hide + */ + @NonNull + public WindowContainerTransaction reparentActivityToTaskFragment( + @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT) + .setReparentContainer(fragmentToken) + .setContainer(activityToken) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** + * Reparents all children of one TaskFragment to another. + * @param oldParent children of this TaskFragment will be reparented. + * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the + * children will be moved to the leaf Task. + * @hide + */ + @NonNull + public WindowContainerTransaction reparentChildren( + @NonNull WindowContainerToken oldParent, + @Nullable WindowContainerToken newParent) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN) + .setContainer(oldParent.asBinder()) + .setReparentContainer(newParent.asBinder()) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** * Merges another WCT into this one. * @param transfer When true, this will transfer everything from other potentially leaving * other in an unusable state. When false, other is left alone, but @@ -705,6 +803,11 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4; public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5; public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6; + public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7; + public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8; + public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9; + public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10; + public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -713,75 +816,98 @@ public final class WindowContainerTransaction implements Parcelable { private final int mType; // Container we are performing the operation on. - private final IBinder mContainer; + @Nullable + private IBinder mContainer; // If this is same as mContainer, then only change position, don't reparent. - private final IBinder mReparent; + @Nullable + private IBinder mReparent; // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. - private final boolean mToTop; + private boolean mToTop; + + @Nullable + private int[] mWindowingModes; + + @Nullable + private int[] mActivityTypes; + + @Nullable + private Bundle mLaunchOptions; - final private int[] mWindowingModes; - final private int[] mActivityTypes; + @Nullable + private Intent mActivityIntent; - private final Bundle mLaunchOptions; + // Used as options for WindowContainerTransaction#createTaskFragment(). + @Nullable + private TaskFragmentCreationParams mTaskFragmentCreationOptions; public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { - return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT, - container, reparent, null, null, toTop, null); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) + .setContainer(container) + .setReparentContainer(reparent) + .setToTop(toTop) + .build(); } public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) { - return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER, - container, container, null, null, toTop, null); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER) + .setContainer(container) + .setReparentContainer(container) + .setToTop(toTop) + .build(); } public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent, IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) { - return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT, - currentParent, newParent, windowingModes, activityTypes, onTop, null); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT) + .setContainer(currentParent) + .setReparentContainer(newParent) + .setWindowingModes(windowingModes) + .setActivityTypes(activityTypes) + .setToTop(onTop) + .build(); } public static HierarchyOp createForSetLaunchRoot(IBinder container, int[] windowingModes, int[] activityTypes) { - return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT, - container, null, windowingModes, activityTypes, false, null); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT) + .setContainer(container) + .setWindowingModes(windowingModes) + .setActivityTypes(activityTypes) + .build(); } public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) { - return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS, - root1, root2, null, null, false, null); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS) + .setContainer(root1) + .setReparentContainer(root2) + .build(); } /** Create a hierarchy op for launching a task. */ public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) { final Bundle fullOptions = options == null ? new Bundle() : options; fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId); - return new HierarchyOp(HIERARCHY_OP_TYPE_LAUNCH_TASK, null, null, null, null, true, - fullOptions); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK) + .setToTop(true) + .setLaunchOptions(fullOptions) + .build(); } /** Create a hierarchy op for setting launch adjacent flag root. */ public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container, boolean clearRoot) { - return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null, - null, null, clearRoot, null); + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT) + .setContainer(container) + .setToTop(clearRoot) + .build(); } - - private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent, - int[] windowingModes, int[] activityTypes, boolean toTop, - @Nullable Bundle launchOptions) { + /** Only creates through {@link Builder}. */ + private HierarchyOp(int type) { mType = type; - mContainer = container; - mReparent = reparent; - mWindowingModes = windowingModes != null ? - Arrays.copyOf(windowingModes, windowingModes.length) : null; - mActivityTypes = activityTypes != null ? - Arrays.copyOf(activityTypes, activityTypes.length) : null; - mToTop = toTop; - mLaunchOptions = launchOptions; } public HierarchyOp(@NonNull HierarchyOp copy) { @@ -792,6 +918,8 @@ public final class WindowContainerTransaction implements Parcelable { mWindowingModes = copy.mWindowingModes; mActivityTypes = copy.mActivityTypes; mLaunchOptions = copy.mLaunchOptions; + mActivityIntent = copy.mActivityIntent; + mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions; } protected HierarchyOp(Parcel in) { @@ -802,6 +930,8 @@ public final class WindowContainerTransaction implements Parcelable { mWindowingModes = in.createIntArray(); mActivityTypes = in.createIntArray(); mLaunchOptions = in.readBundle(); + mActivityIntent = in.readTypedObject(Intent.CREATOR); + mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR); } public int getType() { @@ -844,6 +974,16 @@ public final class WindowContainerTransaction implements Parcelable { return mLaunchOptions; } + @Nullable + public Intent getActivityIntent() { + return mActivityIntent; + } + + @Nullable + public TaskFragmentCreationParams getTaskFragmentCreationOptions() { + return mTaskFragmentCreationOptions; + } + @Override public String toString() { switch (mType) { @@ -868,6 +1008,19 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop + "}"; + case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: + return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}"; + case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: + return "{DeleteTaskFragment: taskFragment=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: + return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent=" + + mActivityIntent + " options=" + mLaunchOptions + "}"; + case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: + return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent + + " activity=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: + return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent + + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes @@ -884,6 +1037,8 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeIntArray(mWindowingModes); dest.writeIntArray(mActivityTypes); dest.writeBundle(mLaunchOptions); + dest.writeTypedObject(mActivityIntent, flags); + dest.writeTypedObject(mTaskFragmentCreationOptions, flags); } @Override @@ -902,5 +1057,96 @@ public final class WindowContainerTransaction implements Parcelable { return new HierarchyOp[size]; } }; + + private static class Builder { + + private final int mType; + + @Nullable + private IBinder mContainer; + + @Nullable + private IBinder mReparent; + + private boolean mToTop; + + @Nullable + private int[] mWindowingModes; + + @Nullable + private int[] mActivityTypes; + + @Nullable + private Bundle mLaunchOptions; + + @Nullable + private Intent mActivityIntent; + + @Nullable + private TaskFragmentCreationParams mTaskFragmentCreationOptions; + + Builder(int type) { + mType = type; + } + + Builder setContainer(@Nullable IBinder container) { + mContainer = container; + return this; + } + + Builder setReparentContainer(@Nullable IBinder reparentContainer) { + mReparent = reparentContainer; + return this; + } + + Builder setToTop(boolean toTop) { + mToTop = toTop; + return this; + } + + Builder setWindowingModes(@Nullable int[] windowingModes) { + mWindowingModes = windowingModes; + return this; + } + + Builder setActivityTypes(@Nullable int[] activityTypes) { + mActivityTypes = activityTypes; + return this; + } + + Builder setLaunchOptions(@Nullable Bundle launchOptions) { + mLaunchOptions = launchOptions; + return this; + } + + Builder setActivityIntent(@Nullable Intent activityIntent) { + mActivityIntent = activityIntent; + return this; + } + + Builder setTaskFragmentCreationOptions( + @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) { + mTaskFragmentCreationOptions = taskFragmentCreationOptions; + return this; + } + + HierarchyOp build() { + final HierarchyOp hierarchyOp = new HierarchyOp(mType); + hierarchyOp.mContainer = mContainer; + hierarchyOp.mReparent = mReparent; + hierarchyOp.mWindowingModes = mWindowingModes != null + ? Arrays.copyOf(mWindowingModes, mWindowingModes.length) + : null; + hierarchyOp.mActivityTypes = mActivityTypes != null + ? Arrays.copyOf(mActivityTypes, mActivityTypes.length) + : null; + hierarchyOp.mToTop = mToTop; + hierarchyOp.mLaunchOptions = mLaunchOptions; + hierarchyOp.mActivityIntent = mActivityIntent; + hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions; + + return hierarchyOp; + } + } } } diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java index 901625b0732c..69d7b4c7d696 100644 --- a/core/java/android/window/WindowContext.java +++ b/core/java/android/window/WindowContext.java @@ -57,8 +57,14 @@ public class WindowContext extends ContextWrapper { * * @param base Base {@link Context} for this new instance. * @param type Window type to be used with this context. - * @param options A bundle used to pass window-related options. - * + * @param options A bundle used to pass window-related options. For example, on device with + * multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window + * using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options. + * Example usage: + * Bundle options = new Bundle(); + * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId); + * Context windowContext = context.createWindowContext(display, type, options); + * @see DisplayAreaInfo#rootDisplayAreaId * @hide */ public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) { diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java index b8619fbcf334..5171adf168ce 100644 --- a/core/java/android/window/WindowProviderService.java +++ b/core/java/android/window/WindowProviderService.java @@ -36,7 +36,6 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams.WindowType; import android.view.WindowManagerImpl; -// TODO(b/159767464): handle #onConfigurationChanged(Configuration) /** * A {@link Service} responsible for showing a non-activity window, such as software keyboards or * accessibility overlay windows. This {@link Service} has similar behavior to @@ -54,6 +53,7 @@ public abstract class WindowProviderService extends Service { private final WindowTokenClient mWindowToken = new WindowTokenClient(); private final WindowContextController mController = new WindowContextController(mWindowToken); private WindowManager mWindowManager; + private boolean mInitialized; /** * Returns the type of this {@link WindowProviderService}. @@ -90,6 +90,20 @@ public abstract class WindowProviderService extends Service { } /** + * Returns the display ID to launch this {@link WindowProviderService}. + * + * @hide + */ + @TestApi + @SuppressLint({"OnNameExpected"}) + // Suppress the lint because it is not a callback and users may override this API to provide + // display. + @NonNull + public int getInitialDisplayId() { + return DEFAULT_DISPLAY; + } + + /** * Attaches this WindowProviderService to the {@code windowToken}. * * @hide @@ -104,19 +118,22 @@ public abstract class WindowProviderService extends Service { public final Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) { final Context context = super.createServiceBaseContext(mainThread, packageInfo); - // Always associate with the default display at initialization. - final Display defaultDisplay = context.getSystemService(DisplayManager.class) - .getDisplay(DEFAULT_DISPLAY); - return context.createTokenContext(mWindowToken, defaultDisplay); + final Display display = context.getSystemService(DisplayManager.class) + .getDisplay(getInitialDisplayId()); + return context.createTokenContext(mWindowToken, display); } - @CallSuper + /** @hide */ @Override - public void onCreate() { - super.onCreate(); - mWindowToken.attachContext(this); - mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions()); - mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this); + protected void attachBaseContext(Context newBase) { + super.attachBaseContext(newBase); + if (!mInitialized) { + mWindowToken.attachContext(this); + mController.attachToDisplayArea(getWindowType(), getDisplayId(), + getWindowContextOptions()); + mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this); + mInitialized = true; + } } @SuppressLint("OnNameExpected") diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 6abf5575d353..c32a268cef5c 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -15,14 +15,22 @@ */ package android.window; +import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets; +import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; +import static android.window.ConfigurationHelper.isDifferentDisplay; +import static android.window.ConfigurationHelper.shouldUpdateResources; + import android.annotation.NonNull; import android.app.ActivityThread; import android.app.IWindowToken; import android.app.ResourcesManager; import android.content.Context; import android.content.res.Configuration; +import android.inputmethodservice.AbstractInputMethodService; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.util.Log; import java.lang.ref.WeakReference; @@ -33,11 +41,13 @@ import java.lang.ref.WeakReference; * {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}. * * @see WindowContext - * @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle) + * @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle) * * @hide */ public class WindowTokenClient extends IWindowToken.Stub { + private static final String TAG = WindowTokenClient.class.getSimpleName(); + /** * Attached {@link Context} for this window token to update configuration and resources. * Initialized by {@link #attachContext(Context)}. @@ -46,11 +56,15 @@ public class WindowTokenClient extends IWindowToken.Stub { private final ResourcesManager mResourcesManager = ResourcesManager.getInstance(); + private final Configuration mConfiguration = new Configuration(); + + private boolean mShouldDumpConfigForIme; + /** * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} * can only attach one {@link Context}. * <p>This method must be called before invoking - * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, + * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, * Bundle, boolean)}.<p/> * * @param context context to be attached @@ -61,6 +75,9 @@ public class WindowTokenClient extends IWindowToken.Stub { throw new IllegalStateException("Context is already attached."); } mContextRef = new WeakReference<>(context); + mConfiguration.setTo(context.getResources().getConfiguration()); + mShouldDumpConfigForIme = Build.IS_DEBUGGABLE + && context instanceof AbstractInputMethodService; } @Override @@ -69,17 +86,48 @@ public class WindowTokenClient extends IWindowToken.Stub { if (context == null) { return; } - final int currentDisplayId = context.getDisplayId(); - final boolean displayChanged = newDisplayId != currentDisplayId; - final Configuration config = context.getResources().getConfiguration(); - final boolean configChanged = config.diff(newConfig) != 0; - if (displayChanged || configChanged) { + final boolean displayChanged = isDifferentDisplay(context.getDisplayId(), newDisplayId); + final boolean shouldUpdateResources = shouldUpdateResources(this, mConfiguration, + newConfig, newConfig /* overrideConfig */, displayChanged, + null /* configChanged */); + + if (!shouldUpdateResources && mShouldDumpConfigForIme) { + Log.d(TAG, "Configuration not dispatch to IME because configuration is up" + + " to date. Current config=" + context.getResources().getConfiguration() + + ", reported config=" + mConfiguration + + ", updated config=" + newConfig); + } + + if (shouldUpdateResources) { // TODO(ag/9789103): update resource manager logic to track non-activity tokens mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId); + if (context instanceof WindowContext) { ActivityThread.currentActivityThread().getHandler().post( () -> ((WindowContext) context).dispatchConfigurationChanged(newConfig)); } + + // Dispatch onConfigurationChanged only if there's a significant public change to + // make it compatible with the original behavior. + final Configuration[] sizeConfigurations = context.getResources() + .getSizeConfigurations(); + final SizeConfigurationBuckets buckets = sizeConfigurations != null + ? new SizeConfigurationBuckets(sizeConfigurations) : null; + final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets); + + if (context instanceof WindowProviderService && diff != 0) { + ActivityThread.currentActivityThread().getHandler().post(() -> + ((WindowProviderService) context).onConfigurationChanged(newConfig)); + } + freeTextLayoutCachesIfNeeded(diff); + if (diff == 0 && mShouldDumpConfigForIme) { + Log.d(TAG, "Configuration not dispatch to IME because configuration has no " + + " public difference with updated config. " + + " Current config=" + context.getResources().getConfiguration() + + ", reported config=" + mConfiguration + + ", updated config=" + newConfig); + } + mConfiguration.setTo(newConfig); } if (displayChanged) { context.updateDisplay(newDisplayId); diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 60a8d802861f..d3224b13e312 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -16,16 +16,20 @@ package com.android.internal.policy; +import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN; +import static android.view.WindowManager.TRANSIT_OPEN; +import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -34,12 +38,18 @@ import android.content.res.Configuration; import android.content.res.ResourceId; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Picture; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; import android.os.SystemProperties; import android.util.Slog; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.TransitionOldType; +import android.view.WindowManager.TransitionType; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; @@ -56,11 +66,17 @@ import java.util.List; /** @hide */ public class TransitionAnimation { + public static final int WALLPAPER_TRANSITION_NONE = 0; + public static final int WALLPAPER_TRANSITION_OPEN = 1; + public static final int WALLPAPER_TRANSITION_CLOSE = 2; + public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3; + public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4; + // These are the possible states for the enter/exit activities during a thumbnail transition - public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0; - public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1; - public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2; - public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3; + private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0; + private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1; + private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2; + private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3; /** * Maximum duration for the clip reveal animation. This is used when there is a lot of movement @@ -72,9 +88,15 @@ public class TransitionAnimation { public static final int DEFAULT_APP_TRANSITION_DURATION = 336; + /** Fraction of animation at which the recents thumbnail stays completely transparent */ + private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f; /** Fraction of animation at which the recents thumbnail becomes completely transparent */ private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f; + /** Interpolator to be used for animations that respond directly to a touch */ + static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = + new PathInterpolator(0.3f, 0f, 0.1f, 1f); + private static final String DEFAULT_PACKAGE = "android"; private final Context mContext; @@ -86,7 +108,9 @@ public class TransitionAnimation { new PathInterpolator(0.3f, 0f, 0.1f, 1f); private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f); private final Interpolator mDecelerateInterpolator; + private final Interpolator mFastOutLinearInInterpolator; private final Interpolator mLinearOutSlowInInterpolator; + private final Interpolator mThumbnailFadeInInterpolator; private final Interpolator mThumbnailFadeOutInterpolator; private final Rect mTmpFromClipRect = new Rect(); private final Rect mTmpToClipRect = new Rect(); @@ -107,8 +131,19 @@ public class TransitionAnimation { mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.decelerate_cubic); + mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_linear_in); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.linear_out_slow_in); + mThumbnailFadeInInterpolator = input -> { + // Linear response for first fraction, then complete after that. + if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) { + return 0f; + } + float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) + / (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION); + return mFastOutLinearInInterpolator.getInterpolation(t); + }; mThumbnailFadeOutInterpolator = input -> { // Linear response for first fraction, then complete after that. if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) { @@ -181,6 +216,13 @@ public class TransitionAnimation { DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); } + @Nullable + public Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) { + final Animation animation = loadCrossProfileAppThumbnailEnterAnimation(); + return prepareThumbnailAnimationWithDuration(animation, appRect.width(), + appRect.height(), 0, null); + } + /** Load animation by resource Id from specific package. */ @Nullable public Animation loadAnimationRes(String packageName, int resId) { @@ -347,8 +389,15 @@ public class TransitionAnimation { } } - public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame, - Rect displayFrame, Rect startRect) { + public Animation createClipRevealAnimationLocked(@TransitionType int transit, + int wallpaperTransit, boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) { + return createClipRevealAnimationLockedCompat( + getTransitCompatType(transit, wallpaperTransit), enter, appFrame, displayFrame, + startRect); + } + + public Animation createClipRevealAnimationLockedCompat(@TransitionOldType int transit, + boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) { final Animation anim; if (enter) { final int appWidth = appFrame.width(); @@ -458,8 +507,14 @@ public class TransitionAnimation { return anim; } - public Animation createScaleUpAnimationLocked(int transit, boolean enter, - Rect containingFrame, Rect startRect) { + public Animation createScaleUpAnimationLocked(@TransitionType int transit, int wallpaperTransit, + boolean enter, Rect containingFrame, Rect startRect) { + return createScaleUpAnimationLockedCompat(getTransitCompatType(transit, wallpaperTransit), + enter, containingFrame, startRect); + } + + public Animation createScaleUpAnimationLockedCompat(@TransitionOldType int transit, + boolean enter, Rect containingFrame, Rect startRect) { Animation a; setupDefaultNextAppTransitionStartRect(startRect, mTmpRect); final int appWidth = containingFrame.width(); @@ -514,12 +569,19 @@ public class TransitionAnimation { return a; } + public Animation createThumbnailEnterExitAnimationLocked(boolean enter, boolean scaleUp, + Rect containingFrame, @TransitionType int transit, int wallpaperTransit, + HardwareBuffer thumbnailHeader, Rect startRect) { + return createThumbnailEnterExitAnimationLockedCompat(enter, scaleUp, containingFrame, + getTransitCompatType(transit, wallpaperTransit), thumbnailHeader, startRect); + } + /** * This animation is created when we are doing a thumbnail transition, for the activity that is * leaving, and the activity that is entering. */ - public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, - Rect containingFrame, int transit, HardwareBuffer thumbnailHeader, + public Animation createThumbnailEnterExitAnimationLockedCompat(boolean enter, boolean scaleUp, + Rect containingFrame, @TransitionOldType int transit, HardwareBuffer thumbnailHeader, Rect startRect) { final int appWidth = containingFrame.width(); final int appHeight = containingFrame.height(); @@ -529,6 +591,7 @@ public class TransitionAnimation { final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight; final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; + final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp); switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { @@ -587,8 +650,8 @@ public class TransitionAnimation { * This alternate animation is created when we are doing a thumbnail transition, for the * activity that is leaving, and the activity that is entering. */ - public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState, - int orientation, int transit, Rect containingFrame, Rect contentInsets, + public Animation createAspectScaledThumbnailEnterExitAnimationLocked(boolean enter, + boolean scaleUp, int orientation, int transit, Rect containingFrame, Rect contentInsets, @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform, Rect startRect, Rect defaultStartRect) { Animation a; @@ -601,11 +664,11 @@ public class TransitionAnimation { final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left; final int thumbStartY = mTmpRect.top - containingFrame.top; + final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp); switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { - final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP; if (freeform && scaleUp) { a = createAspectScaledThumbnailEnterFreeformAnimationLocked( containingFrame, surfaceInsets, startRect, defaultStartRect); @@ -720,10 +783,151 @@ public class TransitionAnimation { } /** + * This animation runs for the thumbnail that gets cross faded with the enter/exit activity + * when a thumbnail is specified with the pending animation override. + */ + public Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, + @Nullable Rect contentInsets, HardwareBuffer thumbnailHeader, int orientation, + Rect startRect, Rect defaultStartRect, boolean scaleUp) { + Animation a; + final int thumbWidthI = thumbnailHeader.getWidth(); + final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; + final int thumbHeightI = thumbnailHeader.getHeight(); + final int appWidth = appRect.width(); + + float scaleW = appWidth / thumbWidth; + getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect); + final float fromX; + float fromY; + final float toX; + float toY; + final float pivotX; + final float pivotY; + if (shouldScaleDownThumbnailTransition(orientation)) { + fromX = mTmpRect.left; + fromY = mTmpRect.top; + + // For the curved translate animation to work, the pivot points needs to be at the + // same absolute position as the one from the real surface. + toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left; + toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top; + pivotX = mTmpRect.width() / 2; + pivotY = appRect.height() / 2 / scaleW; + if (mGridLayoutRecentsEnabled) { + // In the grid layout, the header is displayed above the thumbnail instead of + // overlapping it. + fromY -= thumbHeightI; + toY -= thumbHeightI * scaleW; + } + } else { + pivotX = 0; + pivotY = 0; + fromX = mTmpRect.left; + fromY = mTmpRect.top; + toX = appRect.left; + toY = appRect.top; + } + if (scaleUp) { + // Animation up from the thumbnail to the full screen + Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY); + scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); + scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + Animation alpha = new AlphaAnimation(1f, 0f); + alpha.setInterpolator(mThumbnailFadeOutInterpolator); + alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + Animation translate = createCurvedMotion(fromX, toX, fromY, toY); + translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); + translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + + mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI); + mTmpToClipRect.set(appRect); + + // Containing frame is in screen space, but we need the clip rect in the + // app space. + mTmpToClipRect.offsetTo(0, 0); + mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW); + mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW); + + if (contentInsets != null) { + mTmpToClipRect.inset((int) (-contentInsets.left * scaleW), + (int) (-contentInsets.top * scaleW), + (int) (-contentInsets.right * scaleW), + (int) (-contentInsets.bottom * scaleW)); + } + + Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); + clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); + clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + + // This AnimationSet uses the Interpolators assigned above. + AnimationSet set = new AnimationSet(false); + set.addAnimation(scale); + if (!mGridLayoutRecentsEnabled) { + // In the grid layout, the header should be shown for the whole animation. + set.addAnimation(alpha); + } + set.addAnimation(translate); + set.addAnimation(clipAnim); + a = set; + } else { + // Animation down from the full screen to the thumbnail + Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY); + scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); + scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + Animation alpha = new AlphaAnimation(0f, 1f); + alpha.setInterpolator(mThumbnailFadeInInterpolator); + alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + Animation translate = createCurvedMotion(toX, fromX, toY, fromY); + translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); + translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION); + + // This AnimationSet uses the Interpolators assigned above. + AnimationSet set = new AnimationSet(false); + set.addAnimation(scale); + if (!mGridLayoutRecentsEnabled) { + // In the grid layout, the header should be shown for the whole animation. + set.addAnimation(alpha); + } + set.addAnimation(translate); + a = set; + + } + return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0, + null); + } + + /** + * Creates an overlay with a background color and a thumbnail for the cross profile apps + * animation. + */ + public HardwareBuffer createCrossProfileAppsThumbnail( + @DrawableRes int thumbnailDrawableRes, Rect frame) { + final int width = frame.width(); + final int height = frame.height(); + + final Picture picture = new Picture(); + final Canvas canvas = picture.beginRecording(width, height); + canvas.drawColor(Color.argb(0.6f, 0, 0, 0)); + final int thumbnailSize = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.cross_profile_apps_thumbnail_size); + final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes); + drawable.setBounds( + (width - thumbnailSize) / 2, + (height - thumbnailSize) / 2, + (width + thumbnailSize) / 2, + (height + thumbnailSize) / 2); + drawable.setTint(mContext.getColor(android.R.color.white)); + drawable.draw(canvas); + picture.endRecording(); + + return Bitmap.createBitmap(picture).getHardwareBuffer(); + } + + /** * Prepares the specified animation with a standard duration, interpolator, etc. */ private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, - int transit) { + @TransitionOldType int transit) { // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. @@ -820,6 +1024,22 @@ public class TransitionAnimation { return anim; } + private static @TransitionOldType int getTransitCompatType(@TransitionType int transit, + int wallpaperTransit) { + if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) { + return TRANSIT_OLD_WALLPAPER_INTRA_OPEN; + } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) { + return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; + } else if (transit == TRANSIT_OPEN) { + return TRANSIT_OLD_ACTIVITY_OPEN; + } else if (transit == TRANSIT_CLOSE) { + return TRANSIT_OLD_ACTIVITY_CLOSE; + } + + // We only do some special handle for above type, so use type NONE for default behavior. + return TRANSIT_OLD_NONE; + } + /** * Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that * the start rect is outside of the target rect, and there is a lot of movement going on. @@ -843,10 +1063,33 @@ public class TransitionAnimation { } /** + * Return the current thumbnail transition state. + */ + private int getThumbnailTransitionState(boolean enter, boolean scaleUp) { + if (enter) { + if (scaleUp) { + return THUMBNAIL_TRANSITION_ENTER_SCALE_UP; + } else { + return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; + } + } else { + if (scaleUp) { + return THUMBNAIL_TRANSITION_EXIT_SCALE_UP; + } else { + return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; + } + } + } + + /** * Prepares the specified animation with a standard duration, interpolator, etc. */ - private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, + public static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight, long duration, Interpolator interpolator) { + if (a == null) { + return null; + } + if (duration > 0) { a.setDuration(duration); } diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index 8d82e33dc29f..5354afbd667b 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -35,7 +35,7 @@ import com.android.internal.view.InlineSuggestionsRequestInfo; * {@hide} */ oneway interface IInputMethod { - void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps, + void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps, int configChanges); void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo, diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 125182cab254..958205f7b8cd 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -230,6 +230,7 @@ cc_library_shared { "libbinderthreadstateutils", "libdmabufinfo", "libgif", + "libgui_window_info_static", "libseccomp_policy", "libgrallocusage", "libscrypt_static", @@ -370,6 +371,7 @@ cc_library_shared { "libinput", "libbinderthreadstateutils", "libsqlite", + "libgui_window_info_static", ], shared_libs: [ // libbinder needs to be shared since it has global state diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h index ec99d6da5b8e..7eb7ac4bc305 100644 --- a/core/jni/android_hardware_input_InputApplicationHandle.h +++ b/core/jni/android_hardware_input_InputApplicationHandle.h @@ -19,7 +19,7 @@ #include <string> -#include <input/InputApplication.h> +#include <gui/InputApplication.h> #include <nativehelper/JNIHelp.h> #include "jni.h" diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 463d909821b1..afc44ff808a9 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -26,14 +26,17 @@ #include <ui/Region.h> #include <utils/threads.h> +#include <gui/WindowInfo.h> #include "android_hardware_input_InputApplicationHandle.h" #include "android_util_Binder.h" #include "core_jni_helpers.h" -#include "input/InputWindow.h" #include "jni.h" namespace android { +using gui::TouchOcclusionMode; +using gui::WindowInfo; + struct WeakRefHandleField { jfieldID ctrl; jmethodID get; @@ -115,9 +118,9 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>"); - mInfo.flags = Flags<InputWindowInfo::Flag>( + mInfo.flags = Flags<WindowInfo::Flag>( env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags)); - mInfo.type = static_cast<InputWindowInfo::Type>( + mInfo.type = static_cast<WindowInfo::Type>( env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType)); mInfo.dispatchingTimeout = std::chrono::milliseconds( env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis)); @@ -159,7 +162,7 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.ownerUid = env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid); mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>"); - mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>( + mInfo.inputFeatures = static_cast<WindowInfo::Feature>( env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures)); mInfo.displayId = env->GetIntField(obj, gInputWindowHandleClassInfo.displayId); diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h index de5bd6ef97f4..635480fc5abc 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.h +++ b/core/jni/android_hardware_input_InputWindowHandle.h @@ -17,14 +17,14 @@ #ifndef _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H #define _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H -#include <input/InputWindow.h> +#include <gui/WindowInfo.h> #include <nativehelper/JNIHelp.h> #include "jni.h" namespace android { -class NativeInputWindowHandle : public InputWindowHandle { +class NativeInputWindowHandle : public gui::WindowInfoHandle { public: NativeInputWindowHandle(jweak objWeak); virtual ~NativeInputWindowHandle(); @@ -37,7 +37,6 @@ private: jweak mObjWeak; }; - extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle( JNIEnv* env, jobject inputWindowHandleObj); diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 6971301cec32..21db198cc2ff 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -22,6 +22,7 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> #include <attestation/HmacKeyManager.h> +#include <gui/constants.h> #include <input/Input.h> #include <nativehelper/ScopedUtfChars.h> #include <utils/Log.h> @@ -56,6 +57,8 @@ static struct { jfieldID toolMajor; jfieldID toolMinor; jfieldID orientation; + jfieldID relativeX; + jfieldID relativeY; } gPointerCoordsClassInfo; static struct { @@ -212,6 +215,12 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor)); outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, + env->GetFloatField(pointerCoordsObj, + gPointerCoordsClassInfo.relativeX)); + outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, + env->GetFloatField(pointerCoordsObj, + gPointerCoordsClassInfo.relativeY)); BitSet64 bits = BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits)); @@ -261,6 +270,12 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer float rawY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y); vec2 transformed = transform.transform(rawX, rawY); + float rawRelX = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + float rawRelY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + // Apply only rotation and scale, not translation. + const vec2 transformedOrigin = transform.transform(0, 0); + const vec2 transformedRel = transform.transform(rawRelX, rawRelY) - transformedOrigin; + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, transformed.x); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, transformed.y); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure, @@ -277,6 +292,8 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR)); env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation, rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeX, transformedRel.x); + env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeY, transformedRel.y); uint64_t outBits = 0; BitSet64 bits = BitSet64(rawPointerCoords->bits); @@ -289,6 +306,8 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR); bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR); bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION); + bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_X); + bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_Y); if (!bits.isEmpty()) { uint32_t packedAxesCount = bits.count(); jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount, @@ -378,9 +397,9 @@ static jlong android_view_MotionEvent_nativeInitialize( flags, edgeFlags, metaState, buttonState, static_cast<MotionClassification>(classification), transform, xPrecision, yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, - AMOTION_EVENT_INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos, - pointerCount, pointerProperties, rawPointerCoords); + AMOTION_EVENT_INVALID_CURSOR_POSITION, INVALID_DISPLAY_SIZE, + INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos, pointerCount, + pointerProperties, rawPointerCoords); return reinterpret_cast<jlong>(event.release()); } @@ -872,6 +891,8 @@ int register_android_view_MotionEvent(JNIEnv* env) { gPointerCoordsClassInfo.toolMajor = GetFieldIDOrDie(env, clazz, "toolMajor", "F"); gPointerCoordsClassInfo.toolMinor = GetFieldIDOrDie(env, clazz, "toolMinor", "F"); gPointerCoordsClassInfo.orientation = GetFieldIDOrDie(env, clazz, "orientation", "F"); + gPointerCoordsClassInfo.relativeX = GetFieldIDOrDie(env, clazz, "relativeX", "F"); + gPointerCoordsClassInfo.relativeY = GetFieldIDOrDie(env, clazz, "relativeY", "F"); clazz = FindClassOrDie(env, "android/view/MotionEvent$PointerProperties"); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e9e79dc30c20..c0de5d96719a 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -62,6 +62,8 @@ namespace android { +using gui::FocusRequest; + static void doThrowNPE(JNIEnv* env) { jniThrowNullPointerException(env, NULL); } @@ -1006,6 +1008,17 @@ static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz, } } +static void nativeSetDisplayFlags(JNIEnv* env, jclass clazz, jlong transactionObj, jobject tokenObj, + jint flags) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + + { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + transaction->setDisplayFlags(token, flags); + } +} + static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz, jlong transactionObj, jobject tokenObj, jint orientation, @@ -1873,6 +1886,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplaySurface }, {"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V", (void*)nativeSetDisplayLayerStack }, + {"nativeSetDisplayFlags", "(JLandroid/os/IBinder;I)V", + (void*)nativeSetDisplayFlags }, {"nativeSetDisplayProjection", "(JLandroid/os/IBinder;IIIIIIIII)V", (void*)nativeSetDisplayProjection }, {"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V", diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto index 1fc4a01936b1..41fecfd6cdb7 100644 --- a/core/proto/android/server/accessibilitytrace.proto +++ b/core/proto/android/server/accessibilitytrace.proto @@ -46,17 +46,17 @@ message AccessibilityTraceProto { /* required: elapsed realtime in nanos since boot of when this entry was logged */ optional fixed64 elapsed_realtime_nanos = 1; optional string calendar_time = 2; - - optional string process_name = 3; - optional string thread_id_name = 4; + repeated string logging_type = 3; + optional string process_name = 4; + optional string thread_id_name = 5; /* where the trace originated */ - optional string where = 5; + optional string where = 6; - optional string calling_pkg = 6; - optional string calling_params = 7; - optional string calling_stacks = 8; + optional string calling_pkg = 7; + optional string calling_params = 8; + optional string calling_stacks = 9; - optional AccessibilityDumpProto accessibility_service = 9; - optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10; + optional AccessibilityDumpProto accessibility_service = 10; + optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 11; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index fa1e9d4afcdf..0121bff3e7ef 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -278,7 +278,7 @@ message PinnedTaskControllerProto { message TaskProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; - optional WindowContainerProto window_container = 1; + optional WindowContainerProto window_container = 1 [deprecated=true]; optional int32 id = 2; reserved 3; // activity optional bool fills_parent = 4; @@ -295,12 +295,12 @@ message TaskProto { optional string real_activity = 13; optional string orig_activity = 14; - optional int32 display_id = 15; + optional int32 display_id = 15 [deprecated=true]; optional int32 root_task_id = 16; - optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"]; + optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType", deprecated=true] ; optional int32 resize_mode = 18 [(.android.typedef) = "android.appwidget.AppWidgetProviderInfo.ResizeModeFlags"]; - optional int32 min_width = 19; - optional int32 min_height = 20; + optional int32 min_width = 19 [deprecated=true]; + optional int32 min_height = 20 [deprecated=true]; optional .android.graphics.RectProto adjusted_bounds = 21; optional .android.graphics.RectProto last_non_fullscreen_bounds = 22; @@ -312,6 +312,18 @@ message TaskProto { optional bool created_by_organizer = 28; optional string affinity = 29; optional bool has_child_pip_activity = 30; + optional TaskFragmentProto task_fragment = 31; +} + +/* represents TaskFragment */ +message TaskFragmentProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional WindowContainerProto window_container = 1; + optional int32 display_id = 2; + optional int32 activity_type = 3 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"]; + optional int32 min_width = 4; + optional int32 min_height = 5; } /* represents ActivityRecordProto */ @@ -493,6 +505,8 @@ message WindowContainerChildProto { optional WindowTokenProto window_token = 7; /* represents a WindowState child */ optional WindowStateProto window = 8; + /* represents a WindowState child */ + optional TaskFragmentProto task_fragment = 9; } /* represents ConfigurationContainer */ diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8dfbdccf5706..f604dc102eb5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3608,8 +3608,8 @@ --> <integer name="config_largeScreenSmallestScreenWidthDp">600</integer> - <!-- True if the device is using leagacy split. --> - <bool name="config_useLegacySplit">true</bool> + <!-- True if the device is using legacy split. --> + <bool name="config_useLegacySplit">false</bool> <!-- True if the device supports running activities on secondary displays. --> <bool name="config_supportsMultiDisplay">true</bool> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2403a605972e..eb4919b61dc2 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3201,10 +3201,84 @@ <public type="string" name="config_defaultRingtoneVibrationSound" id="0x0104003b" /> <!-- =============================================================== + Resources added in version S-V2 of the platform + + NOTE: add <public> elements within a <staging-public-group> like so: + + <staging-public-group type="attr" first-id="0x01ff0000"> + <public name="exampleAttr1" /> + <public name="exampleAttr2" /> + </staging-public-group> + + To add a new <staging-public-group> block, find the id value for the + last <staging-public-group> block defined for thie API level, and + subtract 0x00010000 from it to get to the id of the new block. + + For example, if the block closest to the end of this file has an id of + 0x01ee0000, the id of the new block should be 0x01ed0000 + (0x01ee0000 - 0x00010000 = 0x01ed0000). + =============================================================== --> + <eat-comment /> + + <staging-public-group type="attr" first-id="0x01ff0000"> + </staging-public-group> + + <staging-public-group type="id" first-id="0x01fe0000"> + </staging-public-group> + + <staging-public-group type="style" first-id="0x01fd0000"> + </staging-public-group> + + <staging-public-group type="string" first-id="0x01fc0000"> + </staging-public-group> + + <staging-public-group type="dimen" first-id="0x01fb0000"> + </staging-public-group> + + <staging-public-group type="color" first-id="0x01fa0000"> + </staging-public-group> + + <staging-public-group type="array" first-id="0x01f90000"> + </staging-public-group> + + <staging-public-group type="drawable" first-id="0x01f80000"> + </staging-public-group> + + <staging-public-group type="layout" first-id="0x01f70000"> + </staging-public-group> + + <staging-public-group type="anim" first-id="0x01f60000"> + </staging-public-group> + + <staging-public-group type="animator" first-id="0x01f50000"> + </staging-public-group> + + <staging-public-group type="interpolator" first-id="0x01f40000"> + </staging-public-group> + + <staging-public-group type="mipmap" first-id="0x01f30000"> + </staging-public-group> + + <staging-public-group type="integer" first-id="0x01f20000"> + </staging-public-group> + + <staging-public-group type="transition" first-id="0x01f10000"> + </staging-public-group> + + <staging-public-group type="raw" first-id="0x01f00000"> + </staging-public-group> + + <staging-public-group type="bool" first-id="0x01ef0000"> + </staging-public-group> + + <staging-public-group type="fraction" first-id="0x01ee0000"> + </staging-public-group> + + <!-- =============================================================== DO NOT ADD UN-GROUPED ITEMS HERE Any new items (attrs, styles, ids, etc.) *must* be added in a - public-group block, as the preceding comment explains. + staging-public-group block, as the preceding comment explains. Items added outside of a group may have their value recalculated every time something new is added to this file. =============================================================== --> diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java index c65ef9a56cd8..53ba140e6aad 100644 --- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java @@ -16,18 +16,40 @@ package android.accessibilityservice; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; +import android.content.Context; import android.content.Intent; +import android.graphics.PixelFormat; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.media.ImageReader; +import android.os.Binder; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.util.SparseArray; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; +import android.window.WindowTokenClient; import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,6 +64,8 @@ import org.mockito.MockitoAnnotations; public class AccessibilityServiceTest { private static final String TAG = "AccessibilityServiceTest"; private static final int CONNECTION_ID = 1; + private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams( + TYPE_ACCESSIBILITY_OVERLAY); private static class AccessibilityServiceTestClass extends AccessibilityService { private IAccessibilityServiceClient mCallback; @@ -49,7 +73,11 @@ public class AccessibilityServiceTest { AccessibilityServiceTestClass() { super(); - attachBaseContext(InstrumentationRegistry.getContext()); + Context context = ApplicationProvider.getApplicationContext(); + final Display display = context.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY); + + attachBaseContext(context.createTokenContext(new WindowTokenClient(), display)); mLooper = InstrumentationRegistry.getContext().getMainLooper(); } @@ -78,14 +106,33 @@ public class AccessibilityServiceTest { private @Mock IBinder mMockIBinder; private IAccessibilityServiceClient mServiceInterface; private AccessibilityServiceTestClass mService; + private final SparseArray<IBinder> mWindowTokens = new SparseArray<>(); @Before - public void setUp() throws RemoteException { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mService = new AccessibilityServiceTestClass(); + mService.onCreate(); mService.setupCallback(mMockClientForCallback); mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent()); mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder); + doAnswer(invocation -> { + Object[] args = invocation.getArguments(); + final int displayId = (int) args[0]; + final IBinder token = new Binder(); + WindowManagerGlobal.getWindowManagerService().addWindowToken(token, + TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */); + mWindowTokens.put(displayId, token); + return token; + }).when(mMockConnection).getOverlayWindowToken(anyInt()); + } + + @After + public void tearDown() throws Exception { + for (int i = mWindowTokens.size() - 1; i >= 0; --i) { + WindowManagerGlobal.getWindowManagerService().removeWindowToken( + mWindowTokens.valueAt(i), mWindowTokens.keyAt(i)); + } } @Test @@ -101,4 +148,79 @@ public class AccessibilityServiceTest { verify(mMockConnection).getSystemActions(); } + + @Test + public void testAddViewWithA11yServiceDerivedDisplayContext() throws Exception { + try (VirtualDisplaySession session = new VirtualDisplaySession()) { + final Context context = mService.createDisplayContext(session.getDisplay()); + InstrumentationRegistry.getInstrumentation().runOnMainSync( + () -> context.getSystemService(WindowManager.class) + .addView(new View(context), mParams) + ); + } + } + + @Test + public void testAddViewWithA11yServiceDerivedWindowContext() throws Exception { + try (VirtualDisplaySession session = new VirtualDisplaySession()) { + final Context context = mService.createDisplayContext(session.getDisplay()) + .createWindowContext(TYPE_ACCESSIBILITY_OVERLAY, null /* options */); + InstrumentationRegistry.getInstrumentation().runOnMainSync( + () -> context.getSystemService(WindowManager.class) + .addView(new View(context), mParams) + ); + } + } + + @Test + public void testAddViewWithA11yServiceDerivedWindowContextWithDisplay() throws Exception { + try (VirtualDisplaySession session = new VirtualDisplaySession()) { + final Context context = mService.createWindowContext(session.getDisplay(), + TYPE_ACCESSIBILITY_OVERLAY, null /* options */); + InstrumentationRegistry.getInstrumentation().runOnMainSync( + () -> context.getSystemService(WindowManager.class) + .addView(new View(context), mParams) + ); + } + } + + @Test(expected = WindowManager.BadTokenException.class) + public void testAddViewWithA11yServiceDerivedWindowContextWithDifferentType() + throws Exception { + try (VirtualDisplaySession session = new VirtualDisplaySession()) { + final Context context = mService.createWindowContext(session.getDisplay(), + TYPE_APPLICATION_OVERLAY, null /* options */); + InstrumentationRegistry.getInstrumentation().runOnMainSync( + () -> context.getSystemService(WindowManager.class) + .addView(new View(context), mParams) + ); + } + } + + + private static class VirtualDisplaySession implements AutoCloseable { + private final VirtualDisplay mVirtualDisplay; + + VirtualDisplaySession() { + final DisplayManager displayManager = ApplicationProvider.getApplicationContext() + .getSystemService(DisplayManager.class); + final int width = 800; + final int height = 480; + final int density = 160; + ImageReader reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, + 2 /* maxImages */); + mVirtualDisplay = displayManager.createVirtualDisplay( + TAG, width, height, density, reader.getSurface(), + VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); + } + + private Display getDisplay() { + return mVirtualDisplay.getDisplay(); + } + + @Override + public void close() throws Exception { + mVirtualDisplay.release(); + } + } } diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java index d1776fb7e5c1..3d7d807ca53d 100644 --- a/core/tests/coretests/src/android/content/ContextTest.java +++ b/core/tests/coretests/src/android/content/ContextTest.java @@ -32,7 +32,6 @@ import android.content.res.Configuration; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; -import android.inputmethodservice.InputMethodService; import android.media.ImageReader; import android.os.UserHandle; import android.view.Display; @@ -140,13 +139,6 @@ public class ContextTest { } @Test - public void testIsUiContext_InputMethodService_returnsTrue() { - final InputMethodService ims = new InputMethodService(); - - assertTrue(ims.isUiContext()); - } - - @Test public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() { verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */); } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 8643a37bba8d..b71d814c508d 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -166,4 +166,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void logTrace(long timestamp, String where, String callingParams, int processId, long threadId, int callingUid, Bundle callingStack) {} + + public void logTrace(long timestamp, String where, long loggingTypes, String callingParams, + int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {} } diff --git a/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java new file mode 100644 index 000000000000..996d7b435e5a --- /dev/null +++ b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2021 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 android.window; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; + +import android.app.ResourcesManager; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Binder; +import android.platform.test.annotations.Presubmit; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +/** + * Tests for {@link ConfigurationHelper} + * + * <p>Build/Install/Run: + * atest FrameworksMockingCoreTests:ConfigurationHelperTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class ConfigurationHelperTest { + MockitoSession mMockitoSession; + ResourcesManager mResourcesManager; + + @Before + public void setUp() { + mMockitoSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(ResourcesManager.class) + .startMocking(); + doReturn(mock(ResourcesManager.class)).when(ResourcesManager::getInstance); + mResourcesManager = ResourcesManager.getInstance(); + } + + @After + public void tearDown() { + mMockitoSession.finishMocking(); + } + + @Test + public void testShouldUpdateResources_NullConfig_ReturnsTrue() { + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), null /* config */, + new Configuration(), new Configuration(), false /* displayChanged */, + null /* configChanged */)).isTrue(); + } + + @Test + public void testShouldUpdateResources_DisplayChanged_ReturnsTrue() { + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(), + new Configuration(), new Configuration(), true /* displayChanged */, + null /* configChanged */)).isTrue(); + } + + @Test + public void testShouldUpdateResources_DifferentResources_ReturnsTrue() { + doReturn(false).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any()); + + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(), + new Configuration(), new Configuration(), false /* displayChanged */, + null /* configChanged */)).isTrue(); + } + + @Test + public void testShouldUpdateResources_DifferentBounds_ReturnsTrue() { + doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any()); + + final Configuration config = new Configuration(); + config.windowConfiguration.setBounds(new Rect(0, 0, 10, 10)); + config.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20)); + + final Configuration newConfig = new Configuration(); + newConfig.windowConfiguration.setBounds(new Rect(0, 0, 20, 20)); + newConfig.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20)); + + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig, + new Configuration(), false /* displayChanged */, null /* configChanged */)) + .isTrue(); + } + + @Test + public void testShouldUpdateResources_SameConfig_ReturnsFalse() { + doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any()); + + final Configuration config = new Configuration(); + final Configuration newConfig = new Configuration(); + + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig, + new Configuration(), false /* displayChanged */, null /* configChanged */)) + .isFalse(); + } + + @Test + public void testShouldUpdateResources_DifferentConfig_ReturnsTrue() { + doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any()); + + final Configuration config = new Configuration(); + final Configuration newConfig = new Configuration(); + newConfig.setToDefaults(); + + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig, + new Configuration(), false /* displayChanged */, null /* configChanged */)) + .isTrue(); + } + + @Test + public void testShouldUpdateResources_DifferentNonPublicConfig_ReturnsTrue() { + doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any()); + + final Configuration config = new Configuration(); + final Configuration newConfig = new Configuration(); + newConfig.windowConfiguration.setAppBounds(new Rect(0, 0, 10, 10)); + + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig, + new Configuration(), false /* displayChanged */, null /* configChanged */)) + .isTrue(); + } + + @Test + public void testShouldUpdateResources_OverrideConfigChanged_ReturnsFalse() { + doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any()); + + final Configuration config = new Configuration(); + final Configuration newConfig = new Configuration(); + final boolean configChanged = true; + + assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig, + new Configuration(), false /* displayChanged */, configChanged)) + .isEqualTo(configChanged); + } +} diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index ac5e2d0fcacb..bfd12f8d76bc 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -103,6 +103,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-2010331310": { + "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-2006946193": { "message": "setClientVisible: %s clientVisible=%b Callers=%s", "level": "VERBOSE", @@ -211,6 +217,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "-1886145147": { + "message": "resumeTopActivity: Going to sleep and all paused", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1884933373": { "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", "level": "INFO", @@ -247,12 +259,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/AppTransition.java" }, - "-1861864501": { - "message": "resumeTopActivityLocked: Going to sleep and all paused", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1844540996": { "message": " Initial targets: %s", "level": "VERBOSE", @@ -325,12 +331,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, - "-1768090656": { - "message": "Re-launching after pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1750384749": { "message": "Launch on display check: allow launch on public display", "level": "DEBUG", @@ -415,12 +415,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1655805455": { - "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1647332198": { "message": "remove RecentTask %s when finishing user %d", "level": "INFO", @@ -433,6 +427,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" }, + "-1633115609": { + "message": "Key dispatch not paused for screen off", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1632122349": { "message": "Changing surface while display frozen: %s", "level": "VERBOSE", @@ -487,6 +487,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "-1564228464": { + "message": "App died while pausing: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1559645910": { "message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s", "level": "DEBUG", @@ -565,12 +571,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityStarter.java" }, - "-1492696222": { - "message": "App died during pause, not stopping: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1480772131": { "message": "No app or window is requesting an orientation, return %d for display id=%d", "level": "VERBOSE", @@ -637,12 +637,24 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1421296808": { + "message": "Moving to RESUMED: %s (in existing)", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1419762046": { "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d", "level": "DEBUG", "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, + "-1419461256": { + "message": "resumeTopActivity: Resumed %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1413901262": { "message": "startRecentsActivity(): intent=%s", "level": "DEBUG", @@ -709,6 +721,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1311436264": { + "message": "Unregister task fragment organizer=%s uid=%d pid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "-1305966693": { "message": "Sending position change to %s, onTop: %b", "level": "VERBOSE", @@ -805,6 +823,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-1187377055": { + "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-1176488860": { "message": "SURFACE isSecure=%b: %s", "level": "INFO", @@ -919,12 +943,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, - "-1066383762": { - "message": "Sleep still waiting to pause %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-1060365734": { "message": "Attempted to add QS dialog window with bad token %s. Aborting.", "level": "WARN", @@ -985,6 +1003,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-957060823": { + "message": "Moving to PAUSING: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-951939129": { "message": "Unregister task organizer=%s uid=%d", "level": "VERBOSE", @@ -1201,6 +1225,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-706481945": { + "message": "TaskFragment parent info changed name=%s parentTaskId=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "-705939410": { "message": "Waiting for pause to complete...", "level": "VERBOSE", @@ -1237,12 +1267,6 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "-672228342": { - "message": "resumeTopActivityLocked: Top activity resumed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-668956537": { "message": " THUMBNAIL %s: CREATE", "level": "INFO", @@ -1267,11 +1291,11 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "-650261962": { - "message": "Sleep needs to pause %s", + "-648891906": { + "message": "Activity not running or entered PiP, resuming next.", "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "-641258376": { "message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...", @@ -1309,12 +1333,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-606328116": { - "message": "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-597091183": { "message": "Delete TaskDisplayArea uid=%d", "level": "VERBOSE", @@ -1375,11 +1393,11 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowAnimator.java" }, - "-533690126": { - "message": "resumeTopActivityLocked: Resumed %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "-542756093": { + "message": "TaskFragment vanished name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" }, "-532081937": { "message": " Commit activity becoming invisible: %s", @@ -1387,11 +1405,11 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "-527683022": { - "message": "resumeTopActivityLocked: Skip resume: some activity pausing.", + "-521613870": { + "message": "App died during pause, not stopping: %s", "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "-519504830": { "message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s", @@ -1477,18 +1495,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, - "-427457280": { - "message": "App died while pausing: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, - "-417514857": { - "message": "Key dispatch not paused for screen off", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-415865166": { "message": "findFocusedWindow: Found new focus @ %s", "level": "VERBOSE", @@ -1597,11 +1603,17 @@ "group": "WM_DEBUG_LOCKTASK", "at": "com\/android\/server\/wm\/LockTaskController.java" }, - "-303497363": { - "message": "reparent: moving activity=%s to task=%d at %d", + "-312353598": { + "message": "Executing finish of activity: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, + "-310337305": { + "message": "Activity config changed during resume: %s, new next: %s", "level": "INFO", - "group": "WM_DEBUG_ADD_REMOVE", - "at": "com\/android\/server\/wm\/ActivityRecord.java" + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "-302468788": { "message": "Expected target rootTask=%s to be top most but found rootTask=%s", @@ -1621,12 +1633,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-279436615": { - "message": "Moving to PAUSING: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-262984451": { "message": "Relaunch failed %s", "level": "INFO", @@ -1639,6 +1645,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-248761393": { + "message": "startPausing: taskFrag =%s mResumedActivity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-240296576": { "message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s", "level": "VERBOSE", @@ -1651,12 +1663,6 @@ "group": "WM_DEBUG_CONFIGURATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-234244777": { - "message": "Activity config changed during resume: %s, new next: %s", - "level": "INFO", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-230587670": { "message": "SyncGroup %d: Unfinished container: %s", "level": "VERBOSE", @@ -1723,12 +1729,6 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-118786523": { - "message": "Resume failed; resetting state to %s: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "-116086365": { "message": "******************** ENABLING SCREEN!", "level": "INFO", @@ -1777,6 +1777,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/Session.java" }, + "-80004683": { + "message": "Resume failed; resetting state to %s: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "-50336993": { "message": "moveFocusableActivityToTop: activity=%s", "level": "DEBUG", @@ -1909,12 +1915,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowContextListenerController.java" }, - "94402792": { - "message": "Moving to RESUMED: %s (in existing)", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "95216706": { "message": "hideIme target: %s ", "level": "DEBUG", @@ -1933,6 +1933,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "102618780": { + "message": "resumeTopActivity: Pausing %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "108170907": { "message": "Add starting %s: startingData=%s", "level": "VERBOSE", @@ -2173,6 +2179,18 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "327461496": { + "message": "Complete pause: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, + "341055768": { + "message": "resumeTopActivity: Skip resume: need to start pausing", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "342460966": { "message": "DRAG %s: pos=(%d,%d)", "level": "INFO", @@ -2227,11 +2245,11 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "391189028": { - "message": "pauseBackTasks: task=%s mResumedActivity=%s", - "level": "DEBUG", + "378825104": { + "message": "Enqueueing pending pause: %s", + "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/TaskDisplayArea.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "397105698": { "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found", @@ -2365,6 +2383,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, + "573582981": { + "message": "reparent: moving activity=%s to new task fragment in task=%d at %d", + "level": "INFO", + "group": "WM_DEBUG_ADD_REMOVE", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "579298675": { "message": "Moving to DESTROYED: %s (removed from history)", "level": "VERBOSE", @@ -2467,6 +2491,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "660908897": { + "message": "Auto-PIP allowed, entering PIP mode directly: %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "662572728": { "message": "Attempted to add a toast window with bad token %s. Aborting.", "level": "WARN", @@ -2485,12 +2515,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "669361121": { + "message": "Sleep still need to stop %d activities", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "674932310": { "message": "Setting Intent of %s to target %s", "level": "VERBOSE", "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/Task.java" }, + "675705156": { + "message": "resumeTopActivity: Top activity resumed %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "685047360": { "message": "Resizing window %s", "level": "VERBOSE", @@ -2521,12 +2563,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" }, - "709500946": { - "message": "resumeTopActivityLocked: Skip resume: need to start pausing", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "715749922": { "message": "Allowlisting %d:%s", "level": "WARN", @@ -2629,12 +2665,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, - "897964776": { - "message": "Complete pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "898863925": { "message": "Attempted to add QS dialog window with unknown token %s. Aborting.", "level": "WARN", @@ -2659,6 +2689,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "935418348": { + "message": "resumeTopActivity: Skip resume: some activity pausing.", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "950074526": { "message": "setLockTaskMode: Can't lock due to auth", "level": "WARN", @@ -2707,11 +2743,11 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" }, - "988389910": { - "message": "resumeTopActivityLocked: Pausing %s", - "level": "DEBUG", + "987903142": { + "message": "Sleep needs to pause %s", + "level": "VERBOSE", "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" + "at": "com\/android\/server\/wm\/TaskFragment.java" }, "996960396": { "message": "Starting Transition %d", @@ -2719,18 +2755,24 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "1001509841": { - "message": "Auto-PIP allowed, entering PIP mode directly: %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1001904964": { "message": "***** BOOT TIMEOUT: forcing display enabled", "level": "WARN", "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1011462000": { + "message": "Re-launching after pause: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, + "1022095595": { + "message": "TaskFragment info changed name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "1023413388": { "message": "Finish waiting for pause of: %s", "level": "VERBOSE", @@ -2917,6 +2959,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1284122013": { + "message": "TaskFragment appeared name=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "1288731814": { "message": "WindowState.hideLw: setting mFocusMayChange true", "level": "INFO", @@ -3163,12 +3211,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/WindowContainer.java" }, - "1585450696": { - "message": "resumeTopActivityLocked: Restarting %s", - "level": "DEBUG", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1589610525": { "message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s", "level": "VERBOSE", @@ -3217,6 +3259,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "1653025361": { + "message": "Register task fragment organizer=%s uid=%d pid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java" + }, "1653210583": { "message": "Removing app %s delayed=%b animation=%s animating=%b", "level": "VERBOSE", @@ -3385,18 +3433,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "1837992242": { - "message": "Executing finish of activity: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, - "1847414670": { - "message": "Activity not running or entered PiP, resuming next.", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1853793312": { "message": "Notify removed startingWindow %s", "level": "VERBOSE", @@ -3409,6 +3445,12 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1856783490": { + "message": "resumeTopActivity: Restarting %s", + "level": "DEBUG", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "1865125884": { "message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b", "level": "DEBUG", @@ -3421,30 +3463,24 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1884961873": { - "message": "Sleep still need to stop %d activities", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1891501279": { "message": "cancelAnimation(): reason=%s", "level": "DEBUG", "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "1894239744": { - "message": "Enqueueing pending pause: %s", - "level": "VERBOSE", - "group": "WM_DEBUG_STATES", - "at": "com\/android\/server\/wm\/Task.java" - }, "1903353011": { "message": "notifyAppStopped: %s", "level": "VERBOSE", "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1912291550": { + "message": "Sleep still waiting to pause %s", + "level": "VERBOSE", + "group": "WM_DEBUG_STATES", + "at": "com\/android\/server\/wm\/TaskFragment.java" + }, "1918448345": { "message": "Task appeared taskId=%d", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml new file mode 100644 index 000000000000..4e2a77f213a0 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/split_outline.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 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.wm.shell.splitscreen.OutlineRoot + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <com.android.wm.shell.splitscreen.OutlineView + android:id="@+id/split_outline" + android:layout_height="match_parent" + android:layout_width="match_parent" /> + +</com.android.wm.shell.splitscreen.OutlineRoot> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index 34c66a4f4b82..bf074b0337ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -97,6 +97,14 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { b.setParent(sc); } + public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) { + final SurfaceControl sc = mLeashes.get(displayId); + if (sc == null) { + throw new IllegalArgumentException("can't find display" + displayId); + } + tx.setPosition(sc, x, y); + } + @Override public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo, @NonNull SurfaceControl leash) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 1861e48482b8..2f3214d1d1ab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -40,6 +40,8 @@ import android.view.ViewTreeObserver; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.wm.shell.common.SyncTransactionQueue; + import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -74,6 +76,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final ShellTaskOrganizer mTaskOrganizer; private final Executor mShellExecutor; + private final SyncTransactionQueue mSyncQueue; private ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; @@ -89,11 +92,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final Rect mTmpRootRect = new Rect(); private final int[] mTmpLocation = new int[2]; - public TaskView(Context context, ShellTaskOrganizer organizer) { + public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) { super(context, null, 0, 0, true /* disableBackgroundLayer */); mTaskOrganizer = organizer; mShellExecutor = organizer.getExecutor(); + mSyncQueue = syncQueue; setUseAlpha(); getHolder().addCallback(this); mGuard.open("release"); @@ -189,8 +193,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setBounds(mTaskToken, mTmpRect); - // TODO(b/151449487): Enable synchronization - mTaskOrganizer.applyTransaction(wct); + mSyncQueue.queue(wct); } /** @@ -236,14 +239,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private void updateTaskVisibility() { WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */); - mTaskOrganizer.applyTransaction(wct); - // TODO(b/151449487): Only call callback once we enable synchronization - if (mListener != null) { - final int taskId = mTaskInfo.taskId; + mSyncQueue.queue(wct); + if (mListener == null) { + return; + } + int taskId = mTaskInfo.taskId; + mSyncQueue.runInSync((t) -> { mListenerExecutor.execute(() -> { mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated); }); - } + }); } @Override @@ -264,10 +269,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, updateTaskVisibility(); } mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true); - // TODO: Synchronize show with the resize onLocationChanged(); if (taskInfo.taskDescription != null) { - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + int backgroundColor = taskInfo.taskDescription.getBackgroundColor(); + mSyncQueue.runInSync((t) -> { + setResizeBackgroundColor(t, backgroundColor); + }); } if (mListener != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java index 58ca1fbaba24..8286d102791e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java @@ -20,8 +20,8 @@ import android.annotation.UiContext; import android.content.Context; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.annotations.ShellMainThread; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -30,12 +30,14 @@ import java.util.function.Consumer; public class TaskViewFactoryController { private final ShellTaskOrganizer mTaskOrganizer; private final ShellExecutor mShellExecutor; + private final SyncTransactionQueue mSyncQueue; private final TaskViewFactory mImpl = new TaskViewFactoryImpl(); public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer, - ShellExecutor shellExecutor) { + ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) { mTaskOrganizer = taskOrganizer; mShellExecutor = shellExecutor; + mSyncQueue = syncQueue; } public TaskViewFactory asTaskViewFactory() { @@ -44,7 +46,7 @@ public class TaskViewFactoryController { /** Creates an {@link TaskView} */ public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) { - TaskView taskView = new TaskView(context, mTaskOrganizer); + TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue); executor.execute(() -> { onCreate.accept(taskView); }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 09fcb86e56de..f3f66dc894ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -85,6 +85,7 @@ import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.PinnedStackListenerForwarder; @@ -137,6 +138,7 @@ public class BubbleController { private final TaskStackListenerImpl mTaskStackListener; private final ShellTaskOrganizer mTaskOrganizer; private final DisplayController mDisplayController; + private final SyncTransactionQueue mSyncQueue; // Used to post to main UI thread private final ShellExecutor mMainExecutor; @@ -209,7 +211,8 @@ public class BubbleController { ShellTaskOrganizer organizer, DisplayController displayController, ShellExecutor mainExecutor, - Handler mainHandler) { + Handler mainHandler, + SyncTransactionQueue syncQueue) { BubbleLogger logger = new BubbleLogger(uiEventLogger); BubblePositioner positioner = new BubblePositioner(context, windowManager); BubbleData data = new BubbleData(context, logger, positioner, mainExecutor); @@ -217,7 +220,7 @@ public class BubbleController { new BubbleDataRepository(context, launcherApps, mainExecutor), statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger, taskStackListener, organizer, positioner, displayController, mainExecutor, - mainHandler); + mainHandler, syncQueue); } /** @@ -239,7 +242,8 @@ public class BubbleController { BubblePositioner positioner, DisplayController displayController, ShellExecutor mainExecutor, - Handler mainHandler) { + Handler mainHandler, + SyncTransactionQueue syncQueue) { mContext = context; mLauncherApps = launcherApps; mBarService = statusBarService == null @@ -262,6 +266,7 @@ public class BubbleController { mSavedBubbleKeysPerUser = new SparseSetArray<>(); mBubbleIconFactory = new BubbleIconFactory(context); mDisplayController = displayController; + mSyncQueue = syncQueue; } public void initialize() { @@ -561,6 +566,10 @@ public class BubbleController { return mTaskOrganizer; } + SyncTransactionQueue getSyncTransactionQueue() { + return mSyncQueue; + } + /** Contains information to help position things on the screen. */ BubblePositioner getPositioner() { return mBubblePositioner; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 9687ec6a8168..a02fa9b18e49 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -25,6 +25,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT; import android.annotation.NonNull; import android.annotation.SuppressLint; @@ -60,7 +61,6 @@ import android.widget.LinearLayout; import androidx.annotation.Nullable; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; import com.android.wm.shell.TaskView; import com.android.wm.shell.common.AlphaOptimizedButton; @@ -77,7 +77,6 @@ public class BubbleExpandedView extends LinearLayout { // The triangle pointing to the expanded view private View mPointerView; - private int mPointerMargin; @Nullable private int[] mExpandedViewContainerLocation; private AlphaOptimizedButton mManageButton; @@ -102,9 +101,6 @@ public class BubbleExpandedView extends LinearLayout { */ private boolean mIsAlphaAnimating = false; - private int mMinHeight; - private int mOverflowHeight; - private int mManageButtonHeight; private int mPointerWidth; private int mPointerHeight; private float mPointerRadius; @@ -338,7 +334,8 @@ public class BubbleExpandedView extends LinearLayout { bringChildToFront(mOverflowView); mManageButton.setVisibility(GONE); } else { - mTaskView = new TaskView(mContext, mController.getTaskOrganizer()); + mTaskView = new TaskView(mContext, mController.getTaskOrganizer(), + mController.getSyncTransactionQueue()); mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener); mExpandedViewContainer.addView(mTaskView); bringChildToFront(mTaskView); @@ -347,12 +344,8 @@ public class BubbleExpandedView extends LinearLayout { void updateDimensions() { Resources res = getResources(); - mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); - mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); - updateFontSize(); - mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerRadius = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_radius); @@ -368,7 +361,6 @@ public class BubbleExpandedView extends LinearLayout { updatePointerView(); } - mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height); if (mManageButton != null) { int visibility = mManageButton.getVisibility(); removeView(mManageButton); @@ -632,12 +624,11 @@ public class BubbleExpandedView extends LinearLayout { } if ((mBubble != null && mTaskView != null) || mIsOverflow) { - float desiredHeight = mIsOverflow - ? mPositioner.isLargeScreen() ? getMaxExpandedHeight() : mOverflowHeight - : mBubble.getDesiredHeight(mContext); - desiredHeight = Math.max(desiredHeight, mMinHeight); - float height = Math.min(desiredHeight, getMaxExpandedHeight()); - height = Math.max(height, mMinHeight); + float desiredHeight = mPositioner.getExpandedViewHeight(mBubble); + int maxHeight = mPositioner.getMaxExpandedViewHeight(mIsOverflow); + float height = desiredHeight == MAX_HEIGHT + ? maxHeight + : Math.min(desiredHeight, maxHeight); FrameLayout.LayoutParams lp = mIsOverflow ? (FrameLayout.LayoutParams) mOverflowView.getLayoutParams() : (FrameLayout.LayoutParams) mTaskView.getLayoutParams(); @@ -661,23 +652,6 @@ public class BubbleExpandedView extends LinearLayout { } } - private int getMaxExpandedHeight() { - int expandedContainerY = mExpandedViewContainerLocation != null - // Remove top insets back here because availableRect.height would account for that - ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top - : 0; - int settingsHeight = mIsOverflow ? 0 : mManageButtonHeight; - int pointerHeight = mPositioner.showBubblesVertically() - ? mPointerWidth - : (int) (mPointerHeight - mPointerOverlap + mPointerMargin); - return mPositioner.getAvailableRect().height() - - expandedContainerY - - getPaddingTop() - - getPaddingBottom() - - settingsHeight - - pointerHeight; - } - /** * Update appearance of the expanded view being displayed. * @@ -727,14 +701,11 @@ public class BubbleExpandedView extends LinearLayout { : mPointerHeight - mPointerOverlap; setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0); - final float expandedViewY = mPositioner.getExpandedViewY(); - // TODO: I don't understand why it works but it does - why normalized in portrait - // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation? - final float normalizedSize = IconNormalizer.getNormalizedCircleSize( - mPositioner.getBubbleSize()); - final float bubbleCenter = showVertically - ? bubblePosition + (mPositioner.getBubbleSize() / 2f) - expandedViewY - : bubblePosition + (normalizedSize / 2f) - mPointerWidth; + // Subtract the expandedViewY here because the pointer is placed within the expandedView. + float pointerPosition = mPositioner.getPointerPosition(bubblePosition); + final float bubbleCenter = mPositioner.showBubblesVertically() + ? pointerPosition - mPositioner.getExpandedViewY(mBubble, bubblePosition) + : pointerPosition; // Post because we need the width of the view post(() -> { float pointerY; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index c600f56ba0c5..0a856a8231a0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -34,6 +34,7 @@ import android.view.WindowMetrics; import androidx.annotation.VisibleForTesting; +import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; import java.lang.annotation.Retention; @@ -58,6 +59,8 @@ public class BubblePositioner { /** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/ public static final int NUM_VISIBLE_WHEN_RESTING = 2; + /** Indicates a bubble's height should be the maximum available space. **/ + public static final int MAX_HEIGHT = -1; private Context mContext; private WindowManager mWindowManager; @@ -68,13 +71,16 @@ public class BubblePositioner { private int mMaxBubbles; private int mBubbleSize; - private int mBubbleBadgeSize; private int mSpacingBetweenBubbles; private int mExpandedViewLargeScreenWidth; private int mExpandedViewPadding; private int mPointerMargin; - private float mPointerWidth; - private float mPointerHeight; + private int mPointerWidth; + private int mPointerHeight; + private int mPointerOverlap; + private int mManageButtonHeight; + private int mExpandedViewMinHeight; + private int mOverflowHeight; private PointF mPinLocation; private PointF mRestingStackPosition; @@ -151,7 +157,6 @@ public class BubblePositioner { Resources res = mContext.getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); - mBubbleBadgeSize = res.getDimensionPixelSize(R.dimen.bubble_badge_size); mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); @@ -161,6 +166,10 @@ public class BubblePositioner { mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); + mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap); + mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height); + mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); + mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mMaxBubbles = calculateMaxBubbles(); @@ -296,8 +305,8 @@ public class BubblePositioner { return mPaddings; } - /** Calculates the y position of the expanded view when it is expanded. */ - public float getExpandedViewY() { + /** Gets the y position of the expanded view if it was top-aligned. */ + private float getExpandedViewYTopAligned() { final int top = getAvailableRect().top; if (showBubblesVertically()) { return top - mPointerWidth; @@ -306,6 +315,116 @@ public class BubblePositioner { } } + /** The maximum height the expanded view can be. */ + public int getMaxExpandedViewHeight(boolean isOverflow) { + int paddingTop = showBubblesVertically() + ? 0 + : mPointerHeight; + int settingsHeight = isOverflow ? 0 : mManageButtonHeight; + // Subtract pointer size because it's laid out in LinearLayout with the expanded view. + int pointerSize = showBubblesVertically() + ? mPointerWidth + : (mPointerHeight + mPointerMargin); + // Subtract top insets because availableRect.height would account for that + int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top; + return getAvailableRect().height() + - expandedContainerY + - paddingTop + - settingsHeight + - pointerSize; + } + + /** + * Determines the height for the bubble, ensuring a minimum height. If the height should be as + * big as available, returns {@link #MAX_HEIGHT}. + */ + public float getExpandedViewHeight(BubbleViewProvider bubble) { + boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey()); + float desiredHeight = isOverflow + ? mOverflowHeight + : ((Bubble) bubble).getDesiredHeight(mContext); + int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight; + desiredHeight = Math.max(manageButtonHeight + desiredHeight, mExpandedViewMinHeight); + if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) { + return MAX_HEIGHT; + } + return desiredHeight; + } + + /** + * Gets the y position for the expanded view. This is the position on screen of the top + * horizontal line of the expanded view. + * + * @param bubble the bubble being positioned. + * @param bubblePosition the x position of the bubble if showing on top, the y position of the + * bubble if showing vertically. + * @return the y position for the expanded view. + */ + public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) { + boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey()); + float expandedViewHeight = getExpandedViewHeight(bubble); + float topAlignment = getExpandedViewYTopAligned(); + if (!showBubblesVertically() || expandedViewHeight == MAX_HEIGHT) { + // Top-align when bubbles are shown at the top or are max size. + return topAlignment; + } + // If we're here, we're showing vertically & developer has made height less than maximum. + int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight; + float pointerPosition = getPointerPosition(bubblePosition); + float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight; + float topIfCentered = pointerPosition - (expandedViewHeight / 2); + if (topIfCentered > mPositionRect.top && mPositionRect.bottom > bottomIfCentered) { + // Center it + return pointerPosition - mPointerWidth - (expandedViewHeight / 2f); + } else if (topIfCentered <= mPositionRect.top) { + // Top align + return topAlignment; + } else { + // Bottom align + return mPositionRect.bottom - manageButtonHeight - expandedViewHeight - mPointerWidth; + } + } + + /** + * The position the pointer points to, the center of the bubble. + * + * @param bubblePosition the x position of the bubble if showing on top, the y position of the + * bubble if showing vertically. + * @return the position the tip of the pointer points to. The x position if showing on top, the + * y position if showing vertically. + */ + public float getPointerPosition(float bubblePosition) { + // TODO: I don't understand why it works but it does - why normalized in portrait + // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation? + final float normalizedSize = IconNormalizer.getNormalizedCircleSize( + getBubbleSize()); + return showBubblesVertically() + ? bubblePosition + (getBubbleSize() / 2f) + : bubblePosition + (normalizedSize / 2f) - mPointerWidth; + } + + /** + * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal + * row. When in landscape or on a large screen, they show at the left or right side in a + * vertical row. This method accounts for screen orientation and will return an x or y value + * for the position of the bubble in the row. + * + * @param index bubble index in the row. + * @param numberOfBubbles the number of bubbles (including the overflow) in the row. + * @return the y position of the bubble if showing vertically and the x position if showing + * horizontally. + */ + public float getBubbleXOrYForOrientation(int index, int numberOfBubbles) { + final float positionInBar = index * (mBubbleSize + mSpacingBetweenBubbles); + final float expandedStackSize = (numberOfBubbles * mBubbleSize) + + ((numberOfBubbles - 1) * mSpacingBetweenBubbles); + final float centerPosition = showBubblesVertically() + ? mPositionRect.centerY() + : mPositionRect.centerX(); + final float rowStart = centerPosition - (expandedStackSize / 2f); + return rowStart + positionInBar; + } + /** * Sets the stack's most recent position along the edge of the screen. This is saved when the * last bubble is removed, so that the stack can be restored in its previous position. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 92e455ce4e3a..2c6136b4f31e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -874,8 +874,10 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); } /* after */); + final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, + getBubbleIndex(mExpandedBubble)); mExpandedViewContainer.setTranslationX(0f); - mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY()); + mExpandedViewContainer.setTranslationY(translationY); mExpandedViewContainer.setAlpha(1f); } removeOnLayoutChangeListener(mOrientationChangedListener); @@ -1524,6 +1526,7 @@ public class BubbleStackView extends FrameLayout bubble.cleanupViews(); } updatePointerPosition(); + updateExpandedView(); logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); return; } @@ -1815,9 +1818,10 @@ public class BubbleStackView extends FrameLayout mTaskbarScrim.setVisibility(VISIBLE); mTaskbarScrim.animate().alpha(1f).start(); } - + final float translationY = mPositioner.getExpandedViewY(mExpandedBubble, + getBubbleIndex(mExpandedBubble)); mExpandedViewContainer.setTranslationX(0f); - mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY()); + mExpandedViewContainer.setTranslationY(translationY); mExpandedViewContainer.setAlpha(1f); int index; @@ -1866,7 +1870,7 @@ public class BubbleStackView extends FrameLayout 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, bubbleWillBeAt + mBubbleSize / 2f, - mPositioner.getExpandedViewY()); + translationY); } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); @@ -1970,7 +1974,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainerMatrix.setScale( 1f, 1f, expandingFromBubbleAt + mBubbleSize / 2f, - mPositioner.getExpandedViewY()); + mPositioner.getExpandedViewY(mExpandedBubble, index)); } mExpandedViewAlphaAnimator.reverse(); @@ -2077,7 +2081,7 @@ public class BubbleStackView extends FrameLayout 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, expandingFromBubbleDestination + mBubbleSize / 2f, - mPositioner.getExpandedViewY()); + mPositioner.getExpandedViewY(mExpandedBubble, expandingFromBubbleDestination)); } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); @@ -2698,7 +2702,9 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); } if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY()); + mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble, + mExpandedAnimationController.getBubbleXOrYForOrientation( + getBubbleIndex(mExpandedBubble)))); mExpandedViewContainer.setTranslationX(0f); mExpandedBubble.getExpandedView().updateView( mExpandedViewContainer.getLocationOnScreen()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index df2b440c19df..efe07fbf108d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -83,12 +83,6 @@ public class ExpandedAnimationController private float mBubblePaddingTop; /** Size of each bubble. */ private float mBubbleSizePx; - /** Max number of bubbles shown in row above expanded view. */ - private int mBubblesMaxRendered; - /** Max amount of space to have between bubbles when expanded. */ - private int mBubblesMaxSpace; - /** Amount of space between the bubbles when expanded. */ - private float mSpaceBetweenBubbles; /** Whether the expand / collapse animation is running. */ private boolean mAnimatingExpand = false; @@ -211,8 +205,6 @@ public class ExpandedAnimationController mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubbleSizePx = mPositioner.getBubbleSize(); - mBubblesMaxRendered = mPositioner.getMaxBubbles(); - mSpaceBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); } /** @@ -628,14 +620,13 @@ public class ExpandedAnimationController } } - // TODO - could move to method on bubblePositioner if mSpaceBetweenBubbles gets moved /** * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal * row. When in landscape or on a large screen, they show at the left or right side in a * vertical row. This method accounts for screen orientation and will return an x or y value * for the position of the bubble in the row. * - * @param index Bubble index in row. + * @param index bubble index in the row. * @return the y position of the bubble if showing vertically and the x position if showing * horizontally. */ @@ -643,15 +634,6 @@ public class ExpandedAnimationController if (mLayout == null) { return 0; } - final float positionInBar = index * (mBubbleSizePx + mSpaceBetweenBubbles); - Rect availableRect = mPositioner.getAvailableRect(); - final boolean isLandscape = mPositioner.showBubblesVertically(); - final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx) - + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles); - final float centerPosition = isLandscape - ? availableRect.centerY() - : availableRect.centerX(); - final float rowStart = centerPosition - (expandedStackSize / 2f); - return rowStart + positionInBar; + return mPositioner.getBubbleXOrYForOrientation(index, mLayout.getChildCount()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index a7996f056785..6f633691ba6d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -279,9 +279,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (!mImeShowing) { removeImeSurface(); } - } - if (mImeSourceControl != null) { - mImeSourceControl.release(SurfaceControl::release); + if (mImeSourceControl != null) { + mImeSourceControl.release(SurfaceControl::release); + } } mImeSourceControl = imeSourceControl; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 5b158d2063ba..8adfac01b462 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -92,12 +92,16 @@ public final class SplitLayout { private DividerSnapAlgorithm mDividerSnapAlgorithm; private int mDividePosition; private boolean mInitialized = false; + private int mOrientation; + private int mRotation; public SplitLayout(String windowName, Context context, Configuration configuration, SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) { mContext = context.createConfigurationContext(configuration); + mOrientation = configuration.orientation; + mRotation = configuration.windowConfiguration.getRotation(); mSplitLayoutHandler = splitLayoutHandler; mDisplayImeController = displayImeController; mSplitWindowManager = new SplitWindowManager( @@ -144,25 +148,36 @@ public final class SplitLayout { /** Applies new configuration, returns {@code false} if there's no effect to the layout. */ public boolean updateConfiguration(Configuration configuration) { - final Rect rootBounds = configuration.windowConfiguration.getBounds(); - if (mRootBounds.equals(rootBounds)) { - return false; + boolean affectsLayout = false; + + // Make sure to render the divider bar with proper resources that matching the screen + // orientation. + final int orientation = configuration.orientation; + if (orientation != mOrientation) { + mOrientation = orientation; + mContext = mContext.createConfigurationContext(configuration); + mSplitWindowManager.setConfiguration(configuration); + affectsLayout = true; } - mContext = mContext.createConfigurationContext(configuration); - mSplitWindowManager.setConfiguration(configuration); - mRootBounds.set(rootBounds); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); - resetDividerPosition(); + // Update the split bounds when necessary. Besides root bounds changed, split bounds need to + // be updated when the rotation changed to cover the case that users rotated the screen 180 + // degrees. + final int rotation = configuration.windowConfiguration.getRotation(); + final Rect rootBounds = configuration.windowConfiguration.getBounds(); + if (rotation != mRotation || !mRootBounds.equals(rootBounds)) { + mRootBounds.set(rootBounds); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); + resetDividerPosition(); + affectsLayout = true; + } - // Don't inflate divider bar if it is not initialized. - if (!mInitialized) { - return false; + if (mInitialized) { + release(); + init(); } - release(); - init(); - return true; + return affectsLayout; } /** Updates recording bounds of divider window and both of the splits. */ @@ -271,7 +286,11 @@ public final class SplitLayout { } private void flingDividePosition(int from, int to) { - if (from == to) return; + if (from == to) { + // No animation run, it should stop resizing here. + mSplitWindowManager.setResizingSplits(false); + return; + } ValueAnimator animator = ValueAnimator .ofInt(from, to) .setDuration(250); @@ -296,9 +315,8 @@ public final class SplitLayout { return context.getSystemService(WindowManager.class) .getMaximumWindowMetrics() .getWindowInsets() - .getInsets(WindowInsets.Type.navigationBars() - | WindowInsets.Type.statusBars() - | WindowInsets.Type.displayCutout()).toRect(); + .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()) + .toRect(); } private static boolean isLandscape(Rect bounds) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java index 362b40f33e89..9bed40d67335 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java @@ -460,6 +460,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, private void stopDragging() { mHandle.setTouching(false, true /* animate */); mWindowManager.setSlippery(true); + mWindowManagerProxy.setResizing(false); releaseBackground(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java index d9409ec2dc17..b1fa2ac25fe7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java @@ -204,7 +204,8 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (transition != mPendingDismiss && transition != mPendingEnter) { // If we're not in split-mode, just abort @@ -239,12 +240,12 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl if (change.getParent() != null) { // This is probably reparented, so we want the parent to be immediately visible final TransitionInfo.Change parentChange = info.getChange(change.getParent()); - t.show(parentChange.getLeash()); - t.setAlpha(parentChange.getLeash(), 1.f); + startTransaction.show(parentChange.getLeash()); + startTransaction.setAlpha(parentChange.getLeash(), 1.f); // and then animate this layer outside the parent (since, for example, this is // the home task animating from fullscreen to part-screen). - t.reparent(leash, info.getRootLeash()); - t.setLayer(leash, info.getChanges().size() - i); + startTransaction.reparent(leash, info.getRootLeash()); + startTransaction.setLayer(leash, info.getChanges().size() - i); // build the finish reparent/reposition mFinishTransaction.reparent(leash, parentChange.getLeash()); mFinishTransaction.setPosition(leash, @@ -271,12 +272,12 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl if (transition == mPendingEnter && mListener.mPrimary.token.equals(change.getContainer()) || mListener.mSecondary.token.equals(change.getContainer())) { - t.setWindowCrop(leash, change.getStartAbsBounds().width(), + startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(), change.getStartAbsBounds().height()); if (mListener.mPrimary.token.equals(change.getContainer())) { // Move layer to top since we want it above the oversized home task during // animation even though home task is on top in hierarchy. - t.setLayer(leash, info.getChanges().size() + 1); + startTransaction.setLayer(leash, info.getChanges().size() + 1); } } boolean isOpening = Transitions.isOpeningType(info.getType()); @@ -289,7 +290,7 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl // Dismissing via snap-to-top/bottom means that the dismissed task is already // not-visible (usually cropped to oblivion) so immediately set its alpha to 0 // and don't animate it so it doesn't pop-in when reparented. - t.setAlpha(leash, 0.f); + startTransaction.setAlpha(leash, 0.f); } else { startExampleAnimation(leash, false /* show */); } @@ -311,7 +312,7 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl } mSplitScreen.finishEnterSplitTransition(homeIsVisible); } - t.apply(); + startTransaction.apply(); onFinish(); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index f367cd608f37..efff3e30f010 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -107,38 +107,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ private static final int ONE_SHOT_ALPHA_ANIMATION_TIMEOUT_MS = 1000; - // Not a complete set of states but serves what we want right now. - private enum State { - UNDEFINED(0), - TASK_APPEARED(1), - ENTRY_SCHEDULED(2), - ENTERING_PIP(3), - ENTERED_PIP(4), - EXITING_PIP(5); - - private final int mStateValue; - - State(int value) { - mStateValue = value; - } - - private boolean isInPip() { - return mStateValue >= TASK_APPEARED.mStateValue - && mStateValue != EXITING_PIP.mStateValue; - } - - /** - * Resize request can be initiated in other component, ignore if we are no longer in PIP, - * still waiting for animation or we're exiting from it. - * - * @return {@code true} if the resize request should be blocked/ignored. - */ - private boolean shouldBlockResizeRequest() { - return mStateValue < ENTERING_PIP.mStateValue - || mStateValue == EXITING_PIP.mStateValue; - } - } - private final Context mContext; private final SyncTransactionQueue mSyncTransactionQueue; private final PipBoundsState mPipBoundsState; @@ -190,7 +158,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final boolean isExitPipDirection = isOutPipDirection(direction) || isRemovePipDirection(direction); - if (mState != State.EXITING_PIP || isExitPipDirection) { + if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP + || isExitPipDirection) { // Finish resize as long as we're not exiting PIP, or, if we are, only if this is // the end of an exit PIP animation. // This is necessary in case there was a resize animation ongoing when exit PIP @@ -233,7 +202,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private ActivityManager.RunningTaskInfo mDeferredTaskInfo; private WindowContainerToken mToken; private SurfaceControl mLeash; - private State mState = State.UNDEFINED; + private PipTransitionState mPipTransitionState; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private long mLastOneShotAlphaAnimationTime; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory @@ -278,6 +247,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public PipTaskOrganizer(Context context, @NonNull SyncTransactionQueue syncTransactionQueue, + @NonNull PipTransitionState pipTransitionState, @NonNull PipBoundsState pipBoundsState, @NonNull PipBoundsAlgorithm boundsHandler, @NonNull PipMenuController pipMenuController, @@ -291,6 +261,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @ShellMainThread ShellExecutor mainExecutor) { mContext = context; mSyncTransactionQueue = syncTransactionQueue; + mPipTransitionState = pipTransitionState; mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; mPipMenuController = pipMenuController; @@ -326,14 +297,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } public boolean isInPip() { - return mState.isInPip(); + return mPipTransitionState.isInPip(); } /** * Returns whether the entry animation is waiting to be started. */ public boolean isEntryScheduled() { - return mState == State.ENTRY_SCHEDULED; + return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED; } /** @@ -401,9 +372,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void exitPip(int animationDurationMs) { - if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) { + if (!mPipTransitionState.isInPip() + || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP + || mToken == null) { Log.wtf(TAG, "Not allowed to exitPip in current state" - + " mState=" + mState + " mToken=" + mToken); + + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken); return; } @@ -427,7 +400,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, wct.setBoundsChangeTransaction(mToken, tx); // Set the exiting state first so if there is fixed rotation later, the running animation // won't be interrupted by alpha animation for existing PiP. - mState = State.EXITING_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); + + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mPipTransitionController.startTransition(destinationBounds, wct); + return; + } mSyncTransactionQueue.queue(wct); mSyncTransactionQueue.runInSync(t -> { // Make sure to grab the latest source hint rect as it could have been @@ -465,9 +443,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Removes PiP immediately. */ public void removePip() { - if (!mState.isInPip() || mToken == null) { + if (!mPipTransitionState.isInPip() || mToken == null) { Log.wtf(TAG, "Not allowed to removePip in current state" - + " mState=" + mState + " mToken=" + mToken); + + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken); return; } @@ -481,10 +459,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setDuration(mExitAnimationDuration); animator.setInterpolator(Interpolators.ALPHA_OUT); animator.start(); - mState = State.EXITING_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); } private void removePipImmediately() { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mToken, null); + wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); + wct.reorder(mToken, false); + mPipTransitionController.startTransition(null, wct); + return; + } + try { // Reset the task bounds first to ensure the activity configuration is reset as well final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -503,7 +490,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Objects.requireNonNull(info, "Requires RunningTaskInfo"); mTaskInfo = info; mToken = mTaskInfo.token; - mState = State.TASK_APPEARED; + mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED); mLeash = leash; mPictureInPictureParams = mTaskInfo.pictureInPictureParams; setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams, @@ -546,6 +533,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { mPipMenuController.attach(mLeash); + } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mOneShotAnimationType = ANIM_TYPE_BOUNDS; } return; } @@ -557,7 +546,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, null /* updateBoundsCallback */); - mState = State.ENTERING_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); mOneShotAnimationType = ANIM_TYPE_BOUNDS; @@ -584,7 +573,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); animateResizePip(currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */); - mState = State.ENTERING_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } /** @@ -609,7 +598,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); - mState = State.ENTRY_SCHEDULED; + mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED); applyEnterPipSyncTransaction(destinationBounds, () -> { mPipAnimationController .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) @@ -620,7 +609,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .start(); // mState is set right after the animation is kicked off to block any resize // requests such as offsetPip that may have been called prior to the transition. - mState = State.ENTERING_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); }, null /* boundsChangeTransaction */); } @@ -667,7 +656,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void sendOnPipTransitionStarted( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { - mState = State.ENTERING_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); } mPipTransitionController.sendOnPipTransitionStarted(direction); } @@ -676,7 +665,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, void sendOnPipTransitionFinished( @PipAnimationController.TransitionDirection int direction) { if (direction == TRANSITION_DIRECTION_TO_PIP) { - mState = State.ENTERED_PIP; + mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); } mPipTransitionController.sendOnPipTransitionFinished(direction); // Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent. @@ -701,7 +690,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { - if (mState == State.UNDEFINED) { + if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } final WindowContainerToken token = info.token; @@ -713,7 +702,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, clearWaitForFixedRotation(); mInSwipePipToHomeTransition = false; mPictureInPictureParams = null; - mState = State.UNDEFINED; + mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); // Re-set the PIP bounds to none. mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); @@ -735,8 +724,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); - if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) { - Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState); + if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP + && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) { + Log.d(TAG, "Defer onTaskInfoChange in current state: " + + mPipTransitionState.getTransitionState()); // Defer applying PiP parameters if the task is entering PiP to avoid disturbing // the animation. mDeferredTaskInfo = info; @@ -769,7 +760,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mNextRotation = newRotation; mWaitForFixedRotation = true; - if (mState.isInPip()) { + if (mPipTransitionState.isInPip()) { // Fade out the existing PiP to avoid jump cut during seamless rotation. fadeExistingPip(false /* show */); } @@ -780,7 +771,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (!mWaitForFixedRotation) { return; } - if (mState == State.TASK_APPEARED) { + if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) { if (mInSwipePipToHomeTransition) { onEndOfSwipePipToHomeTransition(); } else { @@ -788,9 +779,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(), mEnterAnimationDuration); } - } else if (mState == State.ENTERED_PIP && mHasFadeOut) { + } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP + && mHasFadeOut) { fadeExistingPip(true /* show */); - } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) { + } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP + && mDeferredAnimEndTransaction != null) { final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController.getCurrentAnimator(); final Rect destinationBounds = animator.getDestinationBounds(); @@ -844,13 +837,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // note that this can be called when swipe-to-home or fixed-rotation is happening. // Skip this entirely if that's the case. final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation - && (mState != State.ENTERED_PIP); + && (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP); if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) { if (DEBUG) { Log.d(TAG, "Skip onMovementBoundsChanged on rotation change" + " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition + " mWaitForFixedRotation=" + mWaitForFixedRotation - + " mState=" + mState); + + " getTransitionState=" + mPipTransitionState.getTransitionState()); } return; } @@ -858,7 +851,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipAnimationController.getCurrentAnimator(); if (animator == null || !animator.isRunning() || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { - final boolean rotatingPip = mState.isInPip() && fromRotation; + final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation; if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) { // The position will be used by fade-in animation when the fixed rotation is done. mPipBoundsState.setBounds(destinationBoundsOut); @@ -991,7 +984,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, int durationMs, Consumer<Rect> updateBoundsCallback) { - if (!mState.isInPip()) { + if (!mPipTransitionState.isInPip()) { // TODO: tend to use shouldBlockResizeRequest here as well but need to consider // the fact that when in exitPip, scheduleAnimateResizePip is executed in the window // container transaction callback and we want to set the mState immediately. @@ -1021,7 +1014,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper .crop(tx, mLeash, toBounds) - .round(tx, mLeash, mState.isInPip()); + .round(tx, mLeash, mPipTransitionState.isInPip()); if (mPipMenuController.isMenuVisible()) { mPipMenuController.resizePipMenu(mLeash, tx, toBounds); } else { @@ -1099,7 +1092,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void scheduleFinishResizePip(Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, Consumer<Rect> updateBoundsCallback) { - if (mState.shouldBlockResizeRequest()) { + if (mPipTransitionState.shouldBlockResizeRequest()) { return; } @@ -1116,7 +1109,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper .crop(tx, mLeash, destinationBounds) .resetScale(tx, mLeash, destinationBounds) - .round(tx, mLeash, mState.isInPip()); + .round(tx, mLeash, mPipTransitionState.isInPip()); return tx; } @@ -1125,7 +1118,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleOffsetPip(Rect originalBounds, int offset, int duration, Consumer<Rect> updateBoundsCallback) { - if (mState.shouldBlockResizeRequest()) { + if (mPipTransitionState.shouldBlockResizeRequest()) { return; } if (mWaitForFixedRotation) { @@ -1383,7 +1376,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mToken=" + mToken + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); - pw.println(innerPrefix + "mState=" + mState); + pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 4759550c35c0..b75cde099bcb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -18,6 +18,8 @@ package com.android.wm.shell.pip; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.WindowManager.TRANSIT_PIP; +import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; @@ -25,6 +27,8 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; +import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; import android.app.TaskInfo; import android.content.Context; @@ -35,6 +39,7 @@ import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import android.window.WindowContainerTransactionCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -49,64 +54,166 @@ import com.android.wm.shell.transition.Transitions; */ public class PipTransition extends PipTransitionController { + private final PipTransitionState mPipTransitionState; private final int mEnterExitAnimationDuration; private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; private Transitions.TransitionFinishCallback mFinishCallback; + private Rect mExitDestinationBounds = new Rect(); public PipTransition(Context context, - PipBoundsState pipBoundsState, PipMenuController pipMenuController, + PipBoundsState pipBoundsState, + PipTransitionState pipTransitionState, + PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, Transitions transitions, @NonNull ShellTaskOrganizer shellTaskOrganizer) { super(pipBoundsState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); + mPipTransitionState = pipTransitionState; mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); } @Override + public void setIsFullAnimation(boolean isFullAnimation) { + setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA); + } + + /** + * Sets the preferred animation type for one time. + * This is typically used to set the animation type to + * {@link PipAnimationController#ANIM_TYPE_ALPHA}. + */ + private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { + mOneShotAnimationType = animationType; + } + + @Override + public void startTransition(Rect destinationBounds, WindowContainerTransaction out) { + if (destinationBounds != null) { + mExitDestinationBounds.set(destinationBounds); + mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this); + } else { + mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this); + } + } + + @Override public boolean startAnimation(@android.annotation.NonNull IBinder transition, @android.annotation.NonNull TransitionInfo info, - @android.annotation.NonNull SurfaceControl.Transaction t, + @android.annotation.NonNull SurfaceControl.Transaction startTransaction, + @android.annotation.NonNull SurfaceControl.Transaction finishTransaction, @android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) { + + if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) { + final TransitionInfo.Change change = info.getChanges().get(0); + mFinishCallback = finishCallback; + startTransaction.apply(); + boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(), + new Rect(mExitDestinationBounds)); + mExitDestinationBounds.setEmpty(); + return success; + } + + if (info.getType() == TRANSIT_REMOVE_PIP) { + startTransaction.apply(); + finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(), + mPipBoundsState.getDisplayBounds()); + finishCallback.onTransitionFinished(null, null); + return true; + } + + // Search for an Enter PiP transition (along with a show wallpaper one) + TransitionInfo.Change enterPip = null; + TransitionInfo.Change wallpaper = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getTaskInfo() != null && change.getTaskInfo().configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED) { - mFinishCallback = finishCallback; - return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t); + enterPip = change; + } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { + wallpaper = change; } } - return false; + if (enterPip == null) { + return false; + } + + // Show the wallpaper if there is a wallpaper change. + if (wallpaper != null) { + startTransaction.show(wallpaper.getLeash()); + startTransaction.setAlpha(wallpaper.getLeash(), 1.f); + } + + mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); + mFinishCallback = finishCallback; + return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), + startTransaction, finishTransaction); } @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - return null; + if (request.getType() == TRANSIT_PIP) { + WindowContainerTransaction wct = new WindowContainerTransaction(); + mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + wct.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED); + wct.setBounds(request.getTriggerTask().token, destinationBounds); + return wct; + } else { + return null; + } } @Override public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx) { + + if (isInPipDirection(direction)) { + mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP); + } WindowContainerTransaction wct = new WindowContainerTransaction(); prepareFinishResizeTransaction(taskInfo, destinationBounds, direction, tx, wct); - mFinishCallback.onTransitionFinished(wct, null); + mFinishCallback.onTransitionFinished(wct, new WindowContainerTransactionCallback() { + @Override + public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) { + t.merge(tx); + t.apply(); + } + }); finishResizeForMenu(destinationBounds); } + private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, + final Rect destinationBounds) { + PipAnimationController.PipTransitionAnimator animator = + mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), + mPipBoundsState.getBounds(), destinationBounds, null, + TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0); + + animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(mEnterExitAnimationDuration) + .start(); + + return true; + } + private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, - final SurfaceControl.Transaction t) { + final SurfaceControl.Transaction startTransaction, + final SurfaceControl.Transaction finishTransaction) { setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams, taskInfo.topActivityInfo); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds(); PipAnimationController.PipTransitionAnimator animator; + finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( @@ -115,8 +222,10 @@ public class PipTransition extends PipTransitionController { currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - t.setAlpha(leash, 0f); - t.apply(); + startTransaction.setAlpha(leash, 0f); + // PiP menu is attached late in the process here to avoid any artifacts on the leash + // caused by addShellRoot when in gesture navigation mode. + mPipMenuController.attach(leash); animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); mOneShotAnimationType = ANIM_TYPE_BOUNDS; @@ -124,6 +233,7 @@ public class PipTransition extends PipTransitionController { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } + startTransaction.apply(); animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -158,6 +268,5 @@ public class PipTransition extends PipTransitionController { } wct.setBounds(taskInfo.token, taskBounds); - wct.setBoundsChangeTransaction(taskInfo.token, tx); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index d801c918973a..dedc56678ef1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -29,6 +29,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.Looper; import android.view.SurfaceControl; +import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.transition.Transitions; @@ -46,6 +47,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH protected final PipBoundsState mPipBoundsState; protected final ShellTaskOrganizer mShellTaskOrganizer; protected final PipMenuController mPipMenuController; + protected final Transitions mTransitions; private final Handler mMainHandler; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); @@ -98,6 +100,22 @@ public abstract class PipTransitionController implements Transitions.TransitionH SurfaceControl.Transaction tx) { } + /** + * Called to inform the transition that the animation should start with the assumption that + * PiP is not animating from its original bounds, but rather a continuation of another + * animation. For example, gesture navigation would first fade out the PiP activity, and the + * transition should be responsible to animate in (such as fade in) the PiP. + */ + public void setIsFullAnimation(boolean isFullAnimation) { + } + + /** + * Called when the Shell wants to starts a transition/animation. + */ + public void startTransition(Rect destinationBounds, WindowContainerTransaction out) { + // Default implementation does nothing. + } + public PipTransitionController(PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, Transitions transitions, @@ -107,6 +125,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH mShellTaskOrganizer = shellTaskOrganizer; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipAnimationController = pipAnimationController; + mTransitions = transitions; mMainHandler = new Handler(Looper.getMainLooper()); if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.addHandler(this); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java new file mode 100644 index 000000000000..d23aada4133c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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.wm.shell.pip; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and + * {@link PipTransition}. + */ +public class PipTransitionState { + + public static final int UNDEFINED = 0; + public static final int TASK_APPEARED = 1; + public static final int ENTRY_SCHEDULED = 2; + public static final int ENTERING_PIP = 3; + public static final int ENTERED_PIP = 4; + public static final int EXITING_PIP = 5; + + // Not a complete set of states but serves what we want right now. + @IntDef(prefix = { "TRANSITION_STATE_" }, value = { + UNDEFINED, + TASK_APPEARED, + ENTRY_SCHEDULED, + ENTERING_PIP, + ENTERED_PIP, + EXITING_PIP + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TransitionState {} + + private @TransitionState int mState; + + public PipTransitionState() { + mState = UNDEFINED; + } + + public void setTransitionState(@TransitionState int state) { + mState = state; + } + + public @TransitionState int getTransitionState() { + return mState; + } + + public boolean isInPip() { + return mState >= TASK_APPEARED + && mState != EXITING_PIP; + } + + /** + * Resize request can be initiated in other component, ignore if we are no longer in PIP, + * still waiting for animation or we're exiting from it. + * + * @return {@code true} if the resize request should be blocked/ignored. + */ + public boolean shouldBlockResizeRequest() { + return mState < ENTERING_PIP + || mState == EXITING_PIP; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 4f3ec96968b2..62b50c55e77c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -67,6 +67,7 @@ import com.android.wm.shell.pip.IPip; import com.android.wm.shell.pip.IPipAnimationListener; import com.android.wm.shell.pip.PinnedStackListenerForwarder; import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipMediaController; @@ -528,6 +529,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb private void setPinnedStackAnimationType(int animationType) { mPipTaskOrganizer.setOneShotAnimationType(animationType); + mPipTransitionController.setIsFullAnimation( + animationType == PipAnimationController.ANIM_TYPE_BOUNDS); } private void setPinnedStackAnimationListener(IPipAnimationListener callback) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java index b7caf72641a3..551476dc9d54 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java @@ -58,7 +58,8 @@ public class TvPipTransition extends PipTransitionController { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @android.annotation.NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java new file mode 100644 index 000000000000..5d5a6e50341a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 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.wm.shell.splitscreen; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Binder; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.view.WindowlessWindowManager; + +import com.android.wm.shell.R; + +import java.util.function.Supplier; + +/** + * Handles drawing outline of the bounds of provided root surface. The outline will be drown with + * the consideration of display insets like status bar, navigation bar and display cutout. + */ +class OutlineManager extends WindowlessWindowManager { + private static final String WINDOW_NAME = "SplitOutlineLayer"; + private final Context mContext; + private final int mOutlineColor; + private final Rect mOutlineBounds = new Rect(); + private final Rect mTmpBounds = new Rect(); + private final Supplier<SurfaceControl> mOutlineSurfaceSupplier; + private SurfaceControlViewHost mViewHost; + + /** + * Constructs {@link #OutlineManager} with indicated outline color for the provided root + * surface. + */ + OutlineManager(Context context, Configuration configuration, + Supplier<SurfaceControl> outlineSurfaceSupplier, int color) { + super(configuration, null /* rootSurface */, null /* hostInputToken */); + mContext = context.createDisplayContext(context.getDisplay()); + mOutlineSurfaceSupplier = outlineSurfaceSupplier; + mOutlineColor = color; + } + + @Override + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + b.setParent(mOutlineSurfaceSupplier.get()); + } + + boolean updateOutlineBounds(Rect rootBounds) { + computeOutlineBounds(mContext, rootBounds, mTmpBounds); + if (mOutlineBounds.equals(mTmpBounds)) { + return false; + } + mOutlineBounds.set(mTmpBounds); + + if (mViewHost == null) { + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + } + if (mViewHost.getView() == null) { + final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) + .inflate(R.layout.split_outline, null); + rootView.updateOutlineBounds(mOutlineBounds, mOutlineColor); + + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + rootBounds.width(), rootBounds.height(), + TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + lp.token = new Binder(); + lp.setTitle(WINDOW_NAME); + lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports + // TRUSTED_OVERLAY for windowless window without input channel. + mViewHost.setView(rootView, lp); + } else { + ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor); + final WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); + lp.width = rootBounds.width(); + lp.height = rootBounds.height(); + mViewHost.relayout(lp); + } + + return true; + } + + private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { + computeDisplayStableBounds(context, outBounds); + outBounds.intersect(rootBounds); + // Offset the coordinate from screen based to surface based. + outBounds.offset(-rootBounds.left, -rootBounds.top); + } + + private static void computeDisplayStableBounds(Context context, Rect outBounds) { + final WindowMetrics windowMetrics = + context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); + outBounds.set(windowMetrics.getBounds()); + outBounds.inset(windowMetrics.getWindowInsets().getInsets( + WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java new file mode 100644 index 000000000000..71d48eeca71d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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.wm.shell.splitscreen; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; + +/** Root layout for holding split outline. */ +public class OutlineRoot extends FrameLayout { + public OutlineRoot(@NonNull Context context) { + super(context); + } + + public OutlineRoot(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + private OutlineView mOutlineView; + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mOutlineView = findViewById(R.id.split_outline); + } + + void updateOutlineBounds(Rect bounds, int color) { + mOutlineView.updateOutlineBounds(bounds, color); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java new file mode 100644 index 000000000000..ea66180e3dd2 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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.wm.shell.splitscreen; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.Region; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.R; + +/** View for drawing split outline. */ +public class OutlineView extends View { + private final Paint mPaint = new Paint(); + private final Rect mBounds = new Rect(); + + public OutlineView(@NonNull Context context) { + super(context); + } + + public OutlineView(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth(getResources() + .getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); + } + + void updateOutlineBounds(Rect bounds, int color) { + if (mBounds.equals(bounds) && mPaint.getColor() == color) return; + mBounds.set(bounds); + mPaint.setColor(color); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mBounds.isEmpty()) return; + final Path path = new Region(mBounds).getBoundaryPath(); + canvas.drawPath(path, mPaint); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index 82f95a4f32ea..a0bdcc3edd5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -16,8 +16,12 @@ package com.android.wm.shell.splitscreen; +import android.annotation.CallSuper; import android.app.ActivityManager; +import android.content.Context; +import android.graphics.Color; import android.graphics.Rect; +import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -28,15 +32,19 @@ import com.android.wm.shell.common.SyncTransactionQueue; /** * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up * here. All other task are launch in the {@link MainStage}. + * * @see StageCoordinator */ class SideStage extends StageTaskListener { private static final String TAG = SideStage.class.getSimpleName(); + private final Context mContext; + private OutlineManager mOutlineManager; - SideStage(ShellTaskOrganizer taskOrganizer, int displayId, + SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, SurfaceSession surfaceSession) { super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); + mContext = context; } void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, @@ -69,4 +77,26 @@ class SideStage extends StageTaskListener { wct.reparent(task.token, newParent, false /* onTop */); return true; } + + @Override + @CallSuper + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + super.onTaskAppeared(taskInfo, leash); + if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { + mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration, + () -> mRootLeash, + Color.YELLOW); + } + } + + @Override + @CallSuper + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + super.onTaskInfoChanged(taskInfo); + if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId + && mRootTaskInfo.isRunning) { + mOutlineManager.updateOutlineBounds( + mRootTaskInfo.configuration.windowConfiguration.getBounds()); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 002bfb6e429f..d6afeba9fed9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -64,6 +64,12 @@ public interface SplitScreen { return null; } + /** + * Called when the keyguard occluded state changes. + * @param occluded Indicates if the keyguard is now occluded. + */ + void onKeyguardOccludedChanged(boolean occluded); + /** Get a string representation of a stage type */ static String stageTypeToString(@StageType int stage) { switch (stage) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 9a457b5fd88e..89e6ca86487f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -157,6 +157,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.exitSplitScreen(); } + public void onKeyguardOccludedChanged(boolean occluded) { + mStageCoordinator.onKeyguardOccludedChanged(occluded); + } + public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide); } @@ -284,6 +288,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mISplitScreen = new ISplitScreenImpl(SplitScreenController.this); return mISplitScreen; } + + @Override + public void onKeyguardOccludedChanged(boolean occluded) { + mMainExecutor.execute(() -> { + SplitScreenController.this.onKeyguardOccludedChanged(occluded); + }); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index c37789ecbc9d..69d0be6abc0b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -84,17 +84,19 @@ class SplitScreenTransitions { } void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) { mFinishCallback = finishCallback; mAnimatingTransition = transition; if (mRemoteHandler != null) { - mRemoteHandler.startAnimation(transition, info, t, mRemoteFinishCB); + mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction, + mRemoteFinishCB); mRemoteHandler = null; return; } - playInternalAnimation(transition, info, t, mainRoot, sideRoot); + playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot); } private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 0264c5a1c55a..c6aacb1ab501 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -44,6 +44,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.graphics.Rect; +import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; import android.os.IBinder; import android.util.Log; @@ -115,7 +116,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); private final DisplayImeController mDisplayImeController; private final SplitScreenTransitions mSplitTransitions; - private boolean mExitSplitScreenOnHide = true; + private boolean mExitSplitScreenOnHide; + private boolean mKeyguardOccluded; // TODO(b/187041611): remove this flag after totally deprecated legacy split /** Whether the device is supporting legacy split or not. */ @@ -150,6 +152,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSyncQueue, mSurfaceSession); mSideStage = new SideStage( + mContext, mTaskOrganizer, mDisplayId, mSideStageListener, @@ -157,6 +160,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSurfaceSession); mDisplayImeController = displayImeController; mRootTDAOrganizer.registerListener(displayId, this); + final DeviceStateManager deviceStateManager = + mContext.getSystemService(DeviceStateManager.class); + deviceStateManager.registerCallback(taskOrganizer.getExecutor(), + new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged)); mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, mOnTransitionAnimationComplete); transitions.addHandler(this); @@ -253,17 +260,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void setSideStagePosition(@SplitPosition int sideStagePosition) { - setSideStagePosition(sideStagePosition, true /* updateVisibility */); + setSideStagePosition(sideStagePosition, true /* updateBounds */); } private void setSideStagePosition(@SplitPosition int sideStagePosition, - boolean updateVisibility) { + boolean updateBounds) { if (mSideStagePosition == sideStagePosition) return; mSideStagePosition = sideStagePosition; sendOnStagePositionChanged(); - if (mSideStageListener.mVisible && updateVisibility) { - onStageVisibilityChanged(mSideStageListener); + if (mSideStageListener.mVisible && updateBounds) { + onBoundsChanged(mSplitLayout); } } @@ -275,6 +282,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } + void onKeyguardOccludedChanged(boolean occluded) { + // Do not exit split directly, because it needs to wait for task info update to determine + // which task should remain on top after split dismissed. + mKeyguardOccluded = occluded; + } + void exitSplitScreen() { exitSplitScreen(null /* childrenToTop */); } @@ -403,10 +416,20 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Divider is only visible if both the main stage and side stages are visible setDividerVisibility(isSplitScreenVisible()); - if (mExitSplitScreenOnHide && !mainStageVisible && !sideStageVisible) { - // Exit split-screen if both stage are not visible. - // TODO: This is only a temporary request from UX and is likely to be removed soon... - exitSplitScreen(); + if (!mainStageVisible && !sideStageVisible) { + if (mExitSplitScreenOnHide + // Don't dismiss staged split when both stages are not visible due to sleeping display, + // like the cases keyguard showing or screen off. + || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) { + exitSplitScreen(); + } + } else if (mKeyguardOccluded) { + // At least one of the stages is visible while keyguard occluded. Dismiss split because + // there's show-when-locked activity showing on top of keyguard. Also make sure the + // task contains show-when-locked activity remains on top after split dismissed. + final StageTaskListener toTop = + mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null); + exitSplitScreen(toTop); } if (mainStageVisible) { @@ -580,11 +603,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) { mDisplayAreaInfo = displayAreaInfo; if (mSplitLayout != null - && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)) { + && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration) + && mMainStage.isActive()) { onBoundsChanged(mSplitLayout); } } + private void onFoldedStateChanged(boolean folded) { + if (folded && mMainStage.isActive()) { + exitSplitScreen(mMainStage); + } + } + private Rect getSideStageBounds() { return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2(); @@ -672,7 +702,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (transition != mSplitTransitions.mPendingDismiss && transition != mSplitTransitions.mPendingEnter) { @@ -717,14 +748,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean shouldAnimate = true; if (mSplitTransitions.mPendingEnter == transition) { - shouldAnimate = startPendingEnterAnimation(transition, info, t); + shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction); } else if (mSplitTransitions.mPendingDismiss == transition) { - shouldAnimate = startPendingDismissAnimation(transition, info, t); + shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction); } if (!shouldAnimate) return false; - mSplitTransitions.playAnimation(transition, info, t, finishCallback, - mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); + mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction, + finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); return true; } @@ -754,7 +785,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Update local states (before animating). setDividerVisibility(true); - setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */); + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */); setSplitsVisible(true); addDividerBarToTransition(info, t, true /* show */); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index c6fb5af7d4be..4eadf8c78287 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -16,6 +16,13 @@ package com.android.wm.shell.transition; +import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; +import static android.app.ActivityOptions.ANIM_CUSTOM; +import static android.app.ActivityOptions.ANIM_NONE; +import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; +import static android.app.ActivityOptions.ANIM_SCALE_UP; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; @@ -29,17 +36,28 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; +import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE; +import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE; +import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN; +import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE; +import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.graphics.Point; import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.os.IBinder; +import android.os.SystemProperties; +import android.os.UserHandle; import android.util.ArrayMap; import android.view.Choreographer; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Transformation; @@ -61,30 +79,53 @@ import java.util.ArrayList; public class DefaultTransitionHandler implements Transitions.TransitionHandler { private static final int MAX_ANIMATION_DURATION = 3000; + /** + * Restrict ability of activities overriding transition animation in a way such that + * an activity can do it only when the transition happens within a same task. + * + * @see android.app.Activity#overridePendingTransition(int, int) + */ + private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY = + "persist.wm.disable_custom_task_animation"; + + /** + * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY + */ + static boolean sDisableCustomTaskAnimationProperty = + SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true); + private final TransactionPool mTransactionPool; + private final Context mContext; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionAnimation mTransitionAnimation; + private final SurfaceSession mSurfaceSession = new SurfaceSession(); + /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); private final Rect mInsets = new Rect(0, 0, 0, 0); private float mTransitionAnimationScaleSetting = 1.0f; + private final int mCurrentUserId; + DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mTransactionPool = transactionPool; + mContext = context; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG); + mCurrentUserId = UserHandle.myUserId(); AttributeCache.init(context); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "start default transition animation, info = %s", info); @@ -100,16 +141,18 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { mAnimations.remove(transition); finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); }; + + final int wallpaperTransit = getWallpaperTransitType(info); for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_CHANGE) { // No default animation for this, so just update bounds/position. - t.setPosition(change.getLeash(), + startTransaction.setPosition(change.getLeash(), change.getEndAbsBounds().left - change.getEndRelOffset().x, change.getEndAbsBounds().top - change.getEndRelOffset().y); if (change.getTaskInfo() != null) { // Skip non-tasks since those usually have null bounds. - t.setWindowCrop(change.getLeash(), + startTransaction.setWindowCrop(change.getLeash(), change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); } } @@ -117,12 +160,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; - Animation a = loadAnimation(info.getType(), info.getFlags(), change); + Animation a = loadAnimation(info, change, wallpaperTransit); if (a != null) { - startAnimInternal(animations, a, change.getLeash(), onAnimFinish); + startAnimInternal(animations, a, change.getLeash(), onAnimFinish, + null /* position */); + + if (info.getAnimationOptions() != null) { + attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions()); + } } } - t.apply(); + startTransaction.apply(); // run finish now in-case there are no animations onAnimFinish.run(); return true; @@ -141,68 +189,111 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } @Nullable - private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) { - // TODO(b/178678389): It should handle more type animation here + private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change, + int wallpaperTransit) { Animation a = null; - final boolean isOpening = Transitions.isOpeningType(type); + final int type = info.getType(); + final int flags = info.getFlags(); final int changeMode = change.getMode(); final int changeFlags = change.getFlags(); + final boolean isOpeningType = Transitions.isOpeningType(type); + final boolean enter = Transitions.isOpeningType(changeMode); + final boolean isTask = change.getTaskInfo() != null; + final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + final int overrideType = options != null ? options.getType() : ANIM_NONE; + final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true; if (type == TRANSIT_RELAUNCH) { a = mTransitionAnimation.createRelaunchAnimation( - change.getStartAbsBounds(), mInsets, change.getEndAbsBounds()); + change.getEndAbsBounds(), mInsets, change.getEndAbsBounds()); } else if (type == TRANSIT_KEYGUARD_GOING_AWAY) { a = mTransitionAnimation.loadKeyguardExitAnimation(flags, (changeFlags & FLAG_SHOW_WALLPAPER) != 0); } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) { a = mTransitionAnimation.loadKeyguardUnoccludeAnimation(); - } else if (changeMode == TRANSIT_OPEN && isOpening) { - if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - // This received a transferred starting window, so don't animate - return null; - } - - if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { - a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */); - } else if (change.getTaskInfo() != null) { - a = mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_taskOpenEnterAnimation); - } else { - a = mTransitionAnimation.loadDefaultAnimationRes( - (changeFlags & FLAG_TRANSLUCENT) == 0 - ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter); - } - } else if (changeMode == TRANSIT_TO_FRONT && isOpening) { - if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - // This received a transferred starting window, so don't animate - return null; - } - - if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { - a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */); + } else if (overrideType == ANIM_CUSTOM + && (canCustomContainer || options.getOverrideTaskTransition())) { + a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter + ? options.getEnterResId() : options.getExitResId()); + } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) { + a = mTransitionAnimation.loadCrossProfileAppEnterAnimation(); + } else if (overrideType == ANIM_CLIP_REVEAL) { + a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter, + change.getEndAbsBounds(), change.getEndAbsBounds(), + options.getTransitionBounds()); + } else if (overrideType == ANIM_SCALE_UP) { + a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter, + change.getEndAbsBounds(), options.getTransitionBounds()); + } else if (overrideType == ANIM_THUMBNAIL_SCALE_UP + || overrideType == ANIM_THUMBNAIL_SCALE_DOWN) { + final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP; + a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp, + change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(), + options.getTransitionBounds()); + } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation + : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation); + } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation + : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation); + } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation + : R.styleable.WindowAnimation_wallpaperOpenExitAnimation); + } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation + : R.styleable.WindowAnimation_wallpaperCloseExitAnimation); + } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { + if (isOpeningType) { + a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter); } else { - a = mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_taskToFrontEnterAnimation); + a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter); } - } else if (changeMode == TRANSIT_CLOSE && !isOpening) { - if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { - a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */); - } else if (change.getTaskInfo() != null) { - a = mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_taskCloseExitAnimation); + } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) { + // This received a transferred starting window, so don't animate + return null; + } else if (type == TRANSIT_OPEN) { + if (isTask) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_taskOpenEnterAnimation + : R.styleable.WindowAnimation_taskOpenExitAnimation); } else { - a = mTransitionAnimation.loadDefaultAnimationRes( - (changeFlags & FLAG_TRANSLUCENT) == 0 - ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit); + if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) { + a = mTransitionAnimation.loadDefaultAnimationRes( + R.anim.activity_translucent_open_enter); + } else { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_activityOpenEnterAnimation + : R.styleable.WindowAnimation_activityOpenExitAnimation); + } } - } else if (changeMode == TRANSIT_TO_BACK && !isOpening) { - if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) { - a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */); + } else if (type == TRANSIT_TO_FRONT) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_taskToFrontEnterAnimation + : R.styleable.WindowAnimation_taskToFrontExitAnimation); + } else if (type == TRANSIT_CLOSE) { + if (isTask) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_taskCloseEnterAnimation + : R.styleable.WindowAnimation_taskCloseExitAnimation); } else { - a = mTransitionAnimation.loadDefaultAnimationAttr( - R.styleable.WindowAnimation_taskToBackExitAnimation); + if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) { + a = mTransitionAnimation.loadDefaultAnimationRes( + R.anim.activity_translucent_close_exit); + } else { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_activityCloseEnterAnimation + : R.styleable.WindowAnimation_activityCloseExitAnimation); + } } + } else if (type == TRANSIT_TO_BACK) { + a = mTransitionAnimation.loadDefaultAnimationAttr(enter + ? R.styleable.WindowAnimation_taskToBackEnterAnimation + : R.styleable.WindowAnimation_taskToBackExitAnimation); } else if (changeMode == TRANSIT_CHANGE) { // In the absence of a specific adapter, we just want to keep everything stationary. a = new AlphaAnimation(1.f, 1.f); @@ -210,17 +301,19 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } if (a != null) { - Rect start = change.getStartAbsBounds(); - Rect end = change.getEndAbsBounds(); + if (!a.isInitialized()) { + Rect end = change.getEndAbsBounds(); + a.initialize(end.width(), end.height(), end.width(), end.height()); + } a.restrictDuration(MAX_ANIMATION_DURATION); - a.initialize(end.width(), end.height(), start.width(), start.height()); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); } return a; } private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim, - @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) { + @NonNull SurfaceControl leash, @NonNull Runnable finishCallback, + @Nullable Point position) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); final Transformation transformation = new Transformation(); @@ -231,11 +324,13 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { va.addUpdateListener(animation -> { final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime()); - applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix); + applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix, + position); }); final Runnable finisher = () -> { - applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix); + applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix, + position); mTransactionPool.release(transaction); mMainExecutor.execute(() -> { @@ -258,9 +353,112 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { mAnimExecutor.execute(va::start); } + private void attachThumbnail(@NonNull ArrayList<Animator> animations, + @NonNull Runnable finishCallback, TransitionInfo.Change change, + TransitionInfo.AnimationOptions options) { + final boolean isTask = change.getTaskInfo() != null; + final boolean isOpen = Transitions.isOpeningType(change.getMode()); + final boolean isClose = Transitions.isClosingType(change.getMode()); + if (isOpen) { + if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) { + attachCrossProfileThunmbnailAnimation(animations, finishCallback, change); + } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) { + attachThumbnailAnimation(animations, finishCallback, change, options); + } + } else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) { + attachThumbnailAnimation(animations, finishCallback, change, options); + } + } + + private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations, + @NonNull Runnable finishCallback, TransitionInfo.Change change) { + final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId + ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge; + final Rect bounds = change.getEndAbsBounds(); + final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail( + thumbnailDrawableRes, bounds); + if (thumbnail == null) { + return; + } + + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession, + change.getLeash(), thumbnail, transaction); + final Animation a = + mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds); + if (a == null) { + return; + } + + final Runnable finisher = () -> { + wt.destroy(transaction); + mTransactionPool.release(transaction); + + finishCallback.run(); + }; + a.restrictDuration(MAX_ANIMATION_DURATION); + a.scaleCurrentDuration(mTransitionAnimationScaleSetting); + startAnimInternal(animations, a, wt.getSurface(), finisher, + new Point(bounds.left, bounds.top)); + } + + private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations, + @NonNull Runnable finishCallback, TransitionInfo.Change change, + TransitionInfo.AnimationOptions options) { + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession, + change.getLeash(), options.getThumbnail(), transaction); + final Rect bounds = change.getEndAbsBounds(); + final int orientation = mContext.getResources().getConfiguration().orientation; + final Animation a = mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(bounds, + mInsets, options.getThumbnail(), orientation, null /* startRect */, + options.getTransitionBounds(), options.getType() == ANIM_THUMBNAIL_SCALE_UP); + + final Runnable finisher = () -> { + wt.destroy(transaction); + mTransactionPool.release(transaction); + + finishCallback.run(); + }; + a.restrictDuration(MAX_ANIMATION_DURATION); + a.scaleCurrentDuration(mTransitionAnimationScaleSetting); + startAnimInternal(animations, a, wt.getSurface(), finisher, null /* position */); + } + + private static int getWallpaperTransitType(TransitionInfo info) { + boolean hasOpenWallpaper = false; + boolean hasCloseWallpaper = false; + + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) { + if (Transitions.isOpeningType(change.getMode())) { + hasOpenWallpaper = true; + } else if (Transitions.isClosingType(change.getMode())) { + hasCloseWallpaper = true; + } + } + } + + if (hasOpenWallpaper && hasCloseWallpaper) { + return Transitions.isOpeningType(info.getType()) + ? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE; + } else if (hasOpenWallpaper) { + return WALLPAPER_TRANSITION_OPEN; + } else if (hasCloseWallpaper) { + return WALLPAPER_TRANSITION_CLOSE; + } else { + return WALLPAPER_TRANSITION_NONE; + } + } + private static void applyTransformation(long time, SurfaceControl.Transaction t, - SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) { + SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix, + Point position) { anim.getTransformation(time, transformation); + if (position != null) { + transformation.getMatrix().postTranslate(position.x, position.y); + } t.setMatrix(leash, transformation.getMatrix(), matrix); t.setAlpha(leash, transformation.getAlpha()); t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index 4da6664aa3dc..6bd805323aa3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -57,7 +57,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { if (mTransition != transition) return false; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote" @@ -70,19 +71,24 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { }; IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { @Override - public void onTransitionFinished(WindowContainerTransaction wct) { + public void onTransitionFinished(WindowContainerTransaction wct, + SurfaceControl.Transaction sct) { if (mRemote.asBinder() != null) { mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); } - mMainExecutor.execute( - () -> finishCallback.onTransitionFinished(wct, null /* wctCB */)); + mMainExecutor.execute(() -> { + if (sct != null) { + finishTransaction.merge(sct); + } + finishCallback.onTransitionFinished(wct, null /* wctCB */); + }); } }; try { if (mRemote.asBinder() != null) { mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */); } - mRemote.startAnimation(transition, info, t, cb); + mRemote.startAnimation(transition, info, startTransaction, cb); } catch (RemoteException e) { Log.e(Transitions.TAG, "Error running remote transition.", e); if (mRemote.asBinder() != null) { @@ -102,7 +108,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { @Override - public void onTransitionFinished(WindowContainerTransaction wct) { + public void onTransitionFinished(WindowContainerTransaction wct, + SurfaceControl.Transaction sct) { mMainExecutor.execute( () -> finishCallback.onTransitionFinished(wct, null /* wctCB */)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index 9bfb261fcb85..f432049f44f6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -56,14 +56,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters = new ArrayList<>(); - private final IBinder.DeathRecipient mTransitionDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - mMainExecutor.execute(() -> mFilters.clear()); - } - }; + private final ArrayMap<IBinder, RemoteDeathHandler> mDeathHandlers = new ArrayMap<>(); RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; @@ -71,7 +64,9 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { void addFiltered(TransitionFilter filter, IRemoteTransition remote) { try { - remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */); + RemoteDeathHandler handler = new RemoteDeathHandler(remote.asBinder()); + remote.asBinder().linkToDeath(handler, 0 /* flags */); + mDeathHandlers.put(remote.asBinder(), handler); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death"); return; @@ -88,7 +83,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } } if (removed) { - remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */); + RemoteDeathHandler handler = mDeathHandlers.remove(remote.asBinder()); + remote.asBinder().unlinkToDeath(handler, 0 /* flags */); } } @@ -99,7 +95,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { IRemoteTransition pendingRemote = mRequestedRemotes.get(transition); if (pendingRemote == null) { @@ -132,11 +129,15 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { }; IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { @Override - public void onTransitionFinished(WindowContainerTransaction wct) { + public void onTransitionFinished(WindowContainerTransaction wct, + SurfaceControl.Transaction sct) { if (remote.asBinder() != null) { remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); } mMainExecutor.execute(() -> { + if (sct != null) { + finishTransaction.merge(sct); + } mRequestedRemotes.remove(transition); finishCallback.onTransitionFinished(wct, null /* wctCB */); }); @@ -146,7 +147,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { if (remote.asBinder() != null) { remote.asBinder().linkToDeath(remoteDied, 0 /* flags */); } - remote.startAnimation(transition, info, t, cb); + remote.startAnimation(transition, info, startTransaction, cb); } catch (RemoteException e) { Log.e(Transitions.TAG, "Error running remote transition.", e); if (remote.asBinder() != null) { @@ -170,7 +171,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { @Override - public void onTransitionFinished(WindowContainerTransaction wct) { + public void onTransitionFinished(WindowContainerTransaction wct, + SurfaceControl.Transaction sct) { mMainExecutor.execute(() -> { if (!mRequestedRemotes.containsKey(mergeTarget)) { Log.e(TAG, "Merged transition finished after it's mergeTarget (the " @@ -200,4 +202,25 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { + " for %s: %s", transition, remote); return new WindowContainerTransaction(); } + + /** NOTE: binder deaths can alter the filter order */ + private class RemoteDeathHandler implements IBinder.DeathRecipient { + private final IBinder mRemote; + + RemoteDeathHandler(IBinder remote) { + mRemote = remote; + } + + @Override + @BinderThread + public void binderDied() { + mMainExecutor.execute(() -> { + for (int i = mFilters.size() - 1; i >= 0; --i) { + if (mRemote.equals(mFilters.get(i).second.asBinder())) { + mFilters.remove(i); + } + } + }); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 60707ccdca30..8ed92dfb39c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -77,6 +77,12 @@ public class Transitions implements RemoteCallable<Transitions> { /** Transition type for launching 2 tasks simultaneously. */ public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2; + /** Transition type for exiting PIP via the Shell, via pressing the expand button. */ + public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3; + + /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */ + public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4; + private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; @@ -382,7 +388,7 @@ public class Transitions implements RemoteCallable<Transitions> { } boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) { - return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, + return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb)); } @@ -566,12 +572,19 @@ public class Transitions implements RemoteCallable<Transitions> { * Starts a transition animation. This is always called if handleRequest returned non-null * for a particular transition. Otherwise, it is only called if no other handler before * it handled the transition. - * + * @param startTransaction the transaction given to the handler to be applied before the + * transition animation. Note the handler is expected to call on + * {@link SurfaceControl.Transaction#apply()} for startTransaction. + * @param finishTransaction the transaction given to the handler to be applied after the + * transition animation. Unlike startTransaction, the handler is NOT + * expected to apply this transaction. The Transition system will + * apply it when finishCallback is called. * @param finishCallback Call this when finished. This MUST be called on main thread. * @return true if transition was handled, false if not (falls-back to default). */ boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback); /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java new file mode 100644 index 000000000000..2c668ed3d84d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.wm.shell.transition; + +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.hardware.HardwareBuffer; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +/** + * Represents a surface that is displayed over a transition surface. + */ +class WindowThumbnail { + + private SurfaceControl mSurfaceControl; + + private WindowThumbnail() {} + + /** Create a thumbnail surface and attach it over a parent surface. */ + static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent, + HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) { + WindowThumbnail windowThumbnail = new WindowThumbnail(); + windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession) + .setParent(parent) + .setName("WindowThumanil : " + parent.toString()) + .setCallsite("WindowThumanil") + .setFormat(PixelFormat.TRANSLUCENT) + .build(); + + GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader); + t.setBuffer(windowThumbnail.mSurfaceControl, graphicBuffer); + t.setColorSpace(windowThumbnail.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB)); + t.setLayer(windowThumbnail.mSurfaceControl, Integer.MAX_VALUE); + t.show(windowThumbnail.mSurfaceControl); + t.apply(); + + return windowThumbnail; + } + + SurfaceControl getSurface() { + return mSurfaceControl; + } + + /** Remove the thumbnail surface and release the surface. */ + void destroy(SurfaceControl.Transaction t) { + if (mSurfaceControl == null) { + return; + } + + t.remove(mSurfaceControl); + t.apply(); + mSurfaceControl.release(); + mSurfaceControl = null; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java index 20ac5bf8fa84..1cbad155ba7b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java @@ -47,6 +47,8 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import com.android.wm.shell.common.HandlerExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable; import org.junit.After; import org.junit.Before; @@ -71,6 +73,8 @@ public class TaskViewTest extends ShellTestCase { ShellTaskOrganizer mOrganizer; @Mock HandlerExecutor mExecutor; + @Mock + SyncTransactionQueue mSyncQueue; SurfaceSession mSession; SurfaceControl mLeash; @@ -99,7 +103,14 @@ public class TaskViewTest extends ShellTestCase { }).when(mExecutor).execute(any()); when(mOrganizer.getExecutor()).thenReturn(mExecutor); - mTaskView = new TaskView(mContext, mOrganizer); + + doAnswer((InvocationOnMock invocationOnMock) -> { + final TransactionRunnable r = invocationOnMock.getArgument(0); + r.runWithTransaction(new SurfaceControl.Transaction()); + return null; + }).when(mSyncQueue).runInSync(any()); + + mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue); mTaskView.setListener(mExecutor, mViewListener); } @@ -112,7 +123,7 @@ public class TaskViewTest extends ShellTestCase { @Test public void testSetPendingListener_throwsException() { - TaskView taskView = new TaskView(mContext, mOrganizer); + TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue); taskView.setListener(mExecutor, mViewListener); try { taskView.setListener(mExecutor, mViewListener); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 952dc31cdaee..e138595c47f3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -61,7 +61,7 @@ public class SplitLayoutTests extends ShellTestCase { mSplitLayout = new SplitLayout( "TestSplitLayout", mContext, - getConfiguration(false), + getConfiguration(), mSplitLayoutHandler, b -> b.setParent(mRootLeash), mDisplayImeController, @@ -71,9 +71,22 @@ public class SplitLayoutTests extends ShellTestCase { @Test @UiThreadTest public void testUpdateConfiguration() { - mSplitLayout.init(); - assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse(); - assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue(); + final Configuration config = getConfiguration(); + + // Verify it returns true if new config won't affect split layout. + assertThat(mSplitLayout.updateConfiguration(config)).isFalse(); + + // Verify updateConfiguration returns true if the orientation changed. + config.orientation = ORIENTATION_LANDSCAPE; + assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration returns true if it rotated. + config.windowConfiguration.setRotation(1); + assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration returns true if the root bounds changed. + config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080)); + assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); } @Test @@ -108,12 +121,13 @@ public class SplitLayoutTests extends ShellTestCase { verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true)); } - private static Configuration getConfiguration(boolean isLandscape) { + private static Configuration getConfiguration() { final Configuration configuration = new Configuration(); configuration.unset(); - configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + configuration.orientation = ORIENTATION_PORTRAIT; + configuration.windowConfiguration.setRotation(0); configuration.windowConfiguration.setBounds( - new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160)); + new Rect(0, 0, 1080, 2160)); return configuration; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 9d7c82bb8550..0270093da938 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -79,6 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; private TestShellExecutor mMainExecutor; private PipBoundsState mPipBoundsState; + private PipTransitionState mPipTransitionState; private PipBoundsAlgorithm mPipBoundsAlgorithm; private ComponentName mComponent1; @@ -90,11 +91,12 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); + mPipTransitionState = new PipTransitionState(); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, new PipSnapAlgorithm()); mMainExecutor = new TestShellExecutor(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, - mMockSyncTransactionQueue, mPipBoundsState, + mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState, mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController, mMockPipSurfaceTransactionHelper, mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java index 56a005642ce2..69ead3ac9cf9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java @@ -33,6 +33,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.SyncTransactionQueue; @@ -46,7 +47,7 @@ import org.mockito.Spy; /** Tests for {@link SideStage} */ @SmallTest @RunWith(AndroidJUnit4.class) -public class SideStageTests { +public class SideStageTests extends ShellTestCase { @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private StageTaskListener.StageListenerCallbacks mCallbacks; @Mock private SyncTransactionQueue mSyncQueue; @@ -60,8 +61,8 @@ public class SideStageTests { public void setup() { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); - mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue, - mSurfaceSession); + mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, + mSyncQueue, mSurfaceSession); mSideStage.onTaskAppeared(mRootTask, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index aca80f3556b9..b6da8681d850 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -102,7 +102,7 @@ public class SplitTransitionTests extends ShellTestCase { mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock( StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); - mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock( + mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, @@ -131,6 +131,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskAppeared(mSideChild, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertTrue(accepted); @@ -168,6 +169,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskAppeared(newTask, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertFalse(accepted); assertTrue(mStageCoordinator.isSplitScreenVisible()); @@ -188,6 +190,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskVanished(newTask); accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertFalse(accepted); assertTrue(mStageCoordinator.isSplitScreenVisible()); @@ -223,6 +226,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskVanished(mSideChild); mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertFalse(mStageCoordinator.isSplitScreenVisible()); } @@ -244,6 +248,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskVanished(mSideChild); boolean accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertTrue(accepted); assertFalse(mStageCoordinator.isSplitScreenVisible()); @@ -274,6 +279,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskVanished(mSideChild); boolean accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertTrue(accepted); assertFalse(mStageCoordinator.isSplitScreenVisible()); @@ -298,6 +304,7 @@ public class SplitTransitionTests extends ShellTestCase { mSideStage.onTaskAppeared(mSideChild, createMockSurface()); mStageCoordinator.startAnimation(enterTransit, enterInfo, mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction()); } @@ -335,10 +342,11 @@ public class SplitTransitionTests extends ShellTestCase { @Override public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback) + SurfaceControl.Transaction startTransaction, + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { mCalled = true; - finishCallback.onTransitionFinished(mRemoteFinishWCT); + finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */); } @Override diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 2d2ab2c9f674..a2b1f6421f8d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -127,11 +127,13 @@ public class ShellTransitionTests { TestTransitionHandler testHandler = new TestTransitionHandler() { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { for (TransitionInfo.Change chg : info.getChanges()) { if (chg.getMode() == TRANSIT_CHANGE) { - return super.startAnimation(transition, info, t, finishCallback); + return super.startAnimation(transition, info, startTransaction, + finishTransaction, finishCallback); } } return false; @@ -211,7 +213,7 @@ public class ShellTransitionTests { SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { remoteCalled[0] = true; - finishCallback.onTransitionFinished(remoteFinishWCT); + finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); } @Override @@ -285,7 +287,7 @@ public class ShellTransitionTests { SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { remoteCalled[0] = true; - finishCallback.onTransitionFinished(null /* wct */); + finishCallback.onTransitionFinished(null /* wct */, null /* sct */); } @Override @@ -332,7 +334,7 @@ public class ShellTransitionTests { SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { remoteCalled[0] = true; - finishCallback.onTransitionFinished(remoteFinishWCT); + finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); } @Override @@ -358,9 +360,11 @@ public class ShellTransitionTests { oneShot.setTransition(transitToken); IBinder anotherToken = new Binder(); assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0), - mock(SurfaceControl.Transaction.class), testFinish)); + mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), + testFinish)); assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0), - mock(SurfaceControl.Transaction.class), testFinish)); + mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), + testFinish)); } @Test @@ -477,7 +481,8 @@ public class ShellTransitionTests { @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { mFinishes.add(finishCallback); return true; diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/global_actions_change_panel.xml deleted file mode 100644 index bc9c203b299a..000000000000 --- a/packages/SystemUI/res/layout/global_actions_change_panel.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 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. - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <TextView - android:id="@+id/global_actions_change_message" - android:layout_width="wrap_content" - android:visibility="gone" - android:layout_height="wrap_content" - android:text="@string/global_actions_change_description" /> - <ImageView - android:id="@+id/global_actions_change_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml index 5588fd391681..2430eec77678 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml @@ -13,12 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/global_actions_container" android:layout_width="match_parent" android:layout_height="match_parent" + android:orientation="vertical" android:gravity="center" android:layout_gravity="center"> <com.android.systemui.globalactions.GlobalActionsLayoutLite @@ -28,11 +29,8 @@ android:orientation="vertical" android:clipChildren="false" android:clipToPadding="false" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - android:layout_weight="1"> + android:background="@drawable/global_actions_lite_background" + android:padding="@dimen/global_actions_lite_padding"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -40,8 +38,6 @@ android:gravity="center" android:translationZ="@dimen/global_actions_translate" android:orientation="horizontal" - android:background="@drawable/global_actions_lite_background" - android:padding="@dimen/global_actions_lite_padding" android:layoutDirection="ltr"> <androidx.constraintlayout.helper.widget.Flow android:id="@+id/list_flow" @@ -57,4 +53,4 @@ app:flow_horizontalStyle="packed"/> </androidx.constraintlayout.widget.ConstraintLayout> </com.android.systemui.globalactions.GlobalActionsLayoutLite> -</androidx.constraintlayout.widget.ConstraintLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index d1ed2a852d38..478bf9aa6afb 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -606,8 +606,6 @@ <!-- Whether wallet view is shown in landscape / seascape orientations --> <bool name="global_actions_show_landscape_wallet_view">false</bool> - <!-- Whether global actions should show an informational message about changes in S --> - <bool name="global_actions_show_change_info">false</bool> <!-- Package name of the preferred system app to perform eSOS action --> <string name="config_preferredEmergencySosPackage" translatable="false"></string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 18388a95fac6..b5156b6f5003 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1121,7 +1121,6 @@ <dimen name="global_actions_button_padding">38dp</dimen> <dimen name="global_actions_corner_radius">28dp</dimen> <dimen name="global_actions_lite_padding">24dp</dimen> - <dimen name="global_actions_info_margin">32dp</dimen> <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 52627c267edb..dd9a6c177b14 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2984,9 +2984,4 @@ <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] --> <string name="ongoing_phone_call_content_description">Ongoing phone call</string> - - <!-- Placeholder for string describing changes in global actions --> - <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string> - <!-- URL for more information about changes in global actions --> - <string name="global_actions_change_url" translatable="false"></string> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl index 277b2e31f7be..de9558ebca47 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl @@ -77,8 +77,17 @@ oneway interface IOverviewProxy { void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17; /** - * Sent IME status changes + * Sent when suggested rotation button could be shown */ - void onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) = 18; + void onRotationProposal(int rotation, boolean isValid) = 18; + + /** + * Sent when disable flags change + */ + void disable(int displayId, int state1, int state2, boolean animate) = 19; + + /** + * Sent when behavior changes. See WindowInsetsController#@Behavior + */ + void onSystemBarAttributesChanged(int displayId, int behavior) = 20; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 7dffc2613956..a624f06110e6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -16,9 +16,14 @@ package com.android.systemui.shared.recents.utilities; +import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; + import android.graphics.Color; +import android.inputmethodservice.InputMethodService; import android.os.Handler; import android.os.Message; +import android.view.Surface; /* Common code */ public class Utilities { @@ -31,6 +36,23 @@ public class Utilities { h.sendMessageAtFrontOfQueue(msg); } + public static boolean isRotationAnimationCCW(int from, int to) { + // All 180deg WM rotation animations are CCW, match that + if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false; + if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW + if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true; + if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true; + if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false; + if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW + if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW + if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true; + if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false; + if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false; + if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW + if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true; + return false; // Default + } + /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */ public static float computeContrastBetweenColors(int bg, int fg) { float bgR = Color.red(bg) / 255f; @@ -58,4 +80,34 @@ public class Utilities { public static float clamp(float value, float min, float max) { return Math.max(min, Math.min(max, value)); } + + /** + * @return updated set of flags from InputMethodService based off {@param oldHints} + * Leaves original hints unmodified + */ + public static int calculateBackDispositionHints(int oldHints, int backDisposition, + boolean imeShown, boolean showImeSwitcher) { + int hints = oldHints; + switch (backDisposition) { + case InputMethodService.BACK_DISPOSITION_DEFAULT: + case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: + case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: + if (imeShown) { + hints |= NAVIGATION_HINT_BACK_ALT; + } else { + hints &= ~NAVIGATION_HINT_BACK_ALT; + } + break; + case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: + hints &= ~NAVIGATION_HINT_BACK_ALT; + break; + } + if (showImeSwitcher) { + hints |= NAVIGATION_HINT_IME_SHOWN; + } else { + hints &= ~NAVIGATION_HINT_IME_SHOWN; + } + + return hints; + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java new file mode 100644 index 000000000000..5581a1c90527 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021 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.shared.recents.utilities; + +import android.view.View; + +/** + * Shows view ripples by toggling the provided Views "pressed" state. + * Ripples 4 times. + */ +public class ViewRippler { + private static final int RIPPLE_OFFSET_MS = 50; + private static final int RIPPLE_INTERVAL_MS = 2000; + private View mRoot; + + public void start(View root) { + stop(); // Stop any pending ripple animations + + mRoot = root; + + // Schedule pending ripples, offset the 1st to avoid problems with visibility change + mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS); + mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS); + mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS); + mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS); + mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS); + } + + public void stop() { + if (mRoot != null) mRoot.removeCallbacks(mRipple); + } + + private final Runnable mRipple = new Runnable() { + @Override + public void run() { // Cause the ripple to fire via false presses + if (!mRoot.isAttachedToWindow()) return; + mRoot.setPressed(true /* pressed */); + mRoot.setPressed(false /* pressed */); + } + }; +}
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index c468e416f8a5..4663a9afcd3d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -112,6 +112,10 @@ public class QuickStepContract { public static final int SYSUI_STATE_IME_SHOWING = 1 << 18; // The window magnification is overlapped with system gesture insets at the bottom. public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19; + // ImeSwitcher is showing + public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20; + // Device dozing/AOD state + public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -133,7 +137,9 @@ public class QuickStepContract { SYSUI_STATE_ONE_HANDED_ACTIVE, SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, SYSUI_STATE_IME_SHOWING, - SYSUI_STATE_MAGNIFICATION_OVERLAP + SYSUI_STATE_MAGNIFICATION_OVERLAP, + SYSUI_STATE_IME_SWITCHER_SHOWING, + SYSUI_STATE_DEVICE_DOZING }) public @interface SystemUiStateFlags {} @@ -162,6 +168,8 @@ public class QuickStepContract { ? "allow_gesture" : ""); str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : ""); str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : ""); + str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : ""); + str.add((flags & SYSUI_STATE_DEVICE_DOZING) != 0 ? "device_dozing" : ""); return str.toString(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index ee55bf0aa8b7..fdd1abebe0ea 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -260,7 +260,7 @@ public class RemoteAnimationAdapterCompat { t.remove(leashMap.valueAt(i)); } t.apply(); - finishCallback.onTransitionFinished(null /* wct */); + finishCallback.onTransitionFinished(null /* wct */, null /* sct */); } catch (RemoteException e) { Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" + " finished callback", e); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 653d73020c4f..a77cc87a4e2c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -17,14 +17,17 @@ package com.android.systemui.shared.system; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionFilter.CONTAINER_ORDER_TOP; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.app.ActivityTaskManager; import android.graphics.Rect; import android.os.IBinder; import android.os.Parcelable; @@ -73,7 +76,7 @@ public class RemoteTransitionCompat implements Parcelable { IRemoteTransitionFinishedCallback finishedCallback) { final Runnable finishAdapter = () -> { try { - finishedCallback.onTransitionFinished(null /* wct */); + finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); } catch (RemoteException e) { Log.e(TAG, "Failed to call transition finished callback", e); } @@ -87,7 +90,7 @@ public class RemoteTransitionCompat implements Parcelable { IRemoteTransitionFinishedCallback finishedCallback) { final Runnable finishAdapter = () -> { try { - finishedCallback.onTransitionFinished(null /* wct */); + finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); } catch (RemoteException e) { Log.e(TAG, "Failed to call transition finished callback", e); } @@ -119,6 +122,7 @@ public class RemoteTransitionCompat implements Parcelable { // This transition is for opening recents, so recents is on-top. We want to draw // the current going-away task on top of recents, though, so move it to front WindowContainerToken pausingTask = null; + SurfaceControl pausingLeash = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) { @@ -135,7 +139,7 @@ public class RemoteTransitionCompat implements Parcelable { } t.apply(); mRecentsSession.setup(controller, info, finishedCallback, pausingTask, - leashMap); + leashMap, mToken); recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0), new Rect()); } @@ -147,7 +151,7 @@ public class RemoteTransitionCompat implements Parcelable { if (!mergeTarget.equals(mToken)) return; if (!mRecentsSession.merge(info, t, recents)) return; try { - finishedCallback.onTransitionFinished(null /* wct */); + finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); } catch (RemoteException e) { Log.e(TAG, "Error merging transition.", e); } @@ -161,9 +165,13 @@ public class RemoteTransitionCompat implements Parcelable { mFilter = new TransitionFilter(); } mFilter.mRequirements = - new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; + new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(), + new TransitionFilter.Requirement()}; mFilter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME; mFilter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; + mFilter.mRequirements[0].mOrder = CONTAINER_ORDER_TOP; + mFilter.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD; + mFilter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; } /** @@ -178,10 +186,11 @@ public class RemoteTransitionCompat implements Parcelable { private TransitionInfo mInfo = null; private SurfaceControl mOpeningLeash = null; private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; + private IBinder mTransition = null; void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info, IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask, - ArrayMap<SurfaceControl, SurfaceControl> leashMap) { + ArrayMap<SurfaceControl, SurfaceControl> leashMap, IBinder transition) { if (mInfo != null) { throw new IllegalStateException("Trying to run a new recents animation while" + " recents is already active."); @@ -191,6 +200,7 @@ public class RemoteTransitionCompat implements Parcelable { mFinishCB = finishCB; mPausingTask = pausingTask; mLeashMap = leashMap; + mTransition = transition; } @SuppressLint("NewApi") @@ -263,10 +273,13 @@ public class RemoteTransitionCompat implements Parcelable { try { if (!toHome && mPausingTask != null && mOpeningLeash == null) { // The gesture went back to opening the app rather than continuing with - // recents, so end the transition by moving the app back to the top. + // recents, so end the transition by moving the app back to the top (and also + // re-showing it's task). final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.reorder(mPausingTask, true /* onTop */); - mFinishCB.onTransitionFinished(wct); + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.show(mInfo.getChange(mPausingTask).getLeash()); + mFinishCB.onTransitionFinished(wct, t); } else { if (mOpeningLeash != null) { // TODO: the launcher animation should handle this @@ -275,7 +288,7 @@ public class RemoteTransitionCompat implements Parcelable { t.setAlpha(mOpeningLeash, 1.f); t.apply(); } - mFinishCB.onTransitionFinished(null /* wct */); + mFinishCB.onTransitionFinished(null /* wct */, null /* sct */); } } catch (RemoteException e) { Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e); @@ -298,6 +311,7 @@ public class RemoteTransitionCompat implements Parcelable { mInfo = null; mOpeningLeash = null; mLeashMap = null; + mTransition = null; } @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { @@ -318,6 +332,23 @@ public class RemoteTransitionCompat implements Parcelable { @Override public boolean removeTask(int taskId) { return mWrapped != null ? mWrapped.removeTask(taskId) : false; } + + /** + * @see IRecentsAnimationController#detachNavigationBarFromApp + */ + @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) { + try { + ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition); + } catch (RemoteException e) { + Log.e(TAG, "Failed to detach the navigation bar from app", e); + } + } + + /** + * @see IRecentsAnimationController#animateNavigationBarToApp(long) + */ + @Override public void animateNavigationBarToApp(long duration) { + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index a5b25097a56e..8f14cd858f1e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -14,6 +14,9 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.RelativeLayout; +import androidx.annotation.IntDef; +import androidx.annotation.VisibleForTesting; + import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.dagger.KeyguardStatusViewScope; import com.android.systemui.R; @@ -22,6 +25,8 @@ import com.android.systemui.plugins.ClockPlugin; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.TimeZone; @@ -37,6 +42,13 @@ public class KeyguardClockSwitch extends RelativeLayout { private static final long CLOCK_IN_MILLIS = 200; private static final long SMARTSPACE_MOVE_MILLIS = 350; + @IntDef({LARGE, SMALL}) + @Retention(RetentionPolicy.SOURCE) + public @interface ClockSize { } + + public static final int LARGE = 0; + public static final int SMALL = 1; + /** * Optional/alternative clock injected via plugin. */ @@ -65,13 +77,13 @@ public class KeyguardClockSwitch extends RelativeLayout { private float mDarkAmount; /** - * Boolean value indicating if notifications are visible on lock screen. Use null to signify - * it is uninitialized. + * Indicates which clock is currently displayed - should be one of {@link ClockSize}. + * Use null to signify it is uninitialized. */ - private Boolean mHasVisibleNotifications = null; + @ClockSize private Integer mDisplayedClockSize = null; - private AnimatorSet mClockInAnim = null; - private AnimatorSet mClockOutAnim = null; + @VisibleForTesting AnimatorSet mClockInAnim = null; + @VisibleForTesting AnimatorSet mClockOutAnim = null; private ObjectAnimator mSmartspaceAnim = null; /** @@ -264,19 +276,17 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * Based upon whether notifications are showing or not, display/hide the large clock and - * the smaller version. + * Display the desired clock and hide the other one + * + * @return true if desired clock appeared and false if it was already visible */ - boolean willSwitchToLargeClock(boolean hasVisibleNotifications) { - if (mHasVisibleNotifications != null - && hasVisibleNotifications == mHasVisibleNotifications) { + boolean switchToClock(@ClockSize int clockSize) { + if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) { return false; } - boolean useLargeClock = !hasVisibleNotifications; - animateClockChange(useLargeClock); - - mHasVisibleNotifications = hasVisibleNotifications; - return useLargeClock; + animateClockChange(clockSize == LARGE); + mDisplayedClockSize = clockSize; + return true; } public Paint getPaint() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 781f34bfb195..e312c71718d5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -19,6 +19,8 @@ package com.android.keyguard; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static com.android.keyguard.KeyguardClockSwitch.LARGE; + import android.app.WallpaperManager; import android.content.res.Resources; import android.text.TextUtils; @@ -247,10 +249,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** - * Set whether or not the lock screen is showing notifications. + * Set which clock should be displayed on the keyguard. The other one will be automatically + * hidden. */ - public void setHasVisibleNotifications(boolean hasVisibleNotifications) { - if (mView.willSwitchToLargeClock(hasVisibleNotifications)) { + public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) { + boolean appeared = mView.switchToClock(clockSize); + if (appeared && clockSize == LARGE) { mLargeClockViewController.animateAppear(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index ca4d73b6de5d..840e8c8cba58 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -42,7 +42,6 @@ import android.view.WindowInsetsAnimation; import android.view.WindowManager; import android.widget.FrameLayout; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; @@ -104,7 +103,6 @@ public class KeyguardSecurityContainer extends FrameLayout { private boolean mIsSecurityViewLeftAligned = true; private boolean mOneHandedMode = false; - private SecurityMode mSecurityMode = SecurityMode.Invalid; private ViewPropertyAnimator mRunningOneHandedAnimator; private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = @@ -248,66 +246,47 @@ public class KeyguardSecurityContainer extends FrameLayout { } void onResume(SecurityMode securityMode, boolean faceAuthEnabled) { - mSecurityMode = securityMode; mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback); updateBiometricRetry(securityMode, faceAuthEnabled); - - updateLayoutForSecurityMode(securityMode); } - void updateLayoutForSecurityMode(SecurityMode securityMode) { - mSecurityMode = securityMode; - mOneHandedMode = canUseOneHandedBouncer(); - - if (mOneHandedMode) { - mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext); - } - + /** + * Sets whether this security container is in one handed mode. If so, it will measure its + * child SecurityViewFlipper in one half of the screen, and move it when tapping on the opposite + * side of the screen. + */ + public void setOneHandedMode(boolean oneHandedMode) { + mOneHandedMode = oneHandedMode; updateSecurityViewGravity(); updateSecurityViewLocation(false); } - /** Update keyguard position based on a tapped X coordinate. */ - public void updateKeyguardPosition(float x) { - if (mOneHandedMode) { - moveBouncerForXCoordinate(x, /* animate= */false); - } + /** Returns whether this security container is in one-handed mode. */ + public boolean isOneHandedMode() { + return mOneHandedMode; } - /** Return whether the one-handed keyguard should be enabled. */ - private boolean canUseOneHandedBouncer() { - // Is it enabled? - if (!getResources().getBoolean( - com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) { - return false; - } - - if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) { - return false; - } - - return getResources().getBoolean(R.bool.can_use_one_handed_bouncer); + /** + * When in one-handed mode, sets if the inner SecurityViewFlipper should be aligned to the + * left-hand side of the screen or not, and whether to animate when moving between the two. + */ + public void setOneHandedModeLeftAligned(boolean leftAligned, boolean animate) { + mIsSecurityViewLeftAligned = leftAligned; + updateSecurityViewLocation(animate); } - /** Read whether the one-handed keyguard should be on the left/right from settings. */ - private boolean isOneHandedKeyguardLeftAligned(Context context) { - try { - return Settings.Global.getInt(context.getContentResolver(), - Settings.Global.ONE_HANDED_KEYGUARD_SIDE) - == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT; - } catch (Settings.SettingNotFoundException ex) { - return true; - } + /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */ + public boolean isOneHandedModeLeftAligned() { + return mIsSecurityViewLeftAligned; } private void updateSecurityViewGravity() { - View securityView = findKeyguardSecurityView(); - - if (securityView == null) { + if (mSecurityViewFlipper == null) { return; } - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams(); + FrameLayout.LayoutParams lp = + (FrameLayout.LayoutParams) mSecurityViewFlipper.getLayoutParams(); if (mOneHandedMode) { lp.gravity = Gravity.LEFT | Gravity.BOTTOM; @@ -315,7 +294,7 @@ public class KeyguardSecurityContainer extends FrameLayout { lp.gravity = Gravity.CENTER_HORIZONTAL; } - securityView.setLayoutParams(lp); + mSecurityViewFlipper.setLayoutParams(lp); } /** @@ -324,14 +303,12 @@ public class KeyguardSecurityContainer extends FrameLayout { * by the security view . */ private void updateSecurityViewLocation(boolean animate) { - View securityView = findKeyguardSecurityView(); - - if (securityView == null) { + if (mSecurityViewFlipper == null) { return; } if (!mOneHandedMode) { - securityView.setTranslationX(0); + mSecurityViewFlipper.setTranslationX(0); return; } @@ -343,7 +320,8 @@ public class KeyguardSecurityContainer extends FrameLayout { int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f); if (animate) { - mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation); + mRunningOneHandedAnimator = + mSecurityViewFlipper.animate().translationX(targetTranslation); mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() { @Override @@ -355,27 +333,10 @@ public class KeyguardSecurityContainer extends FrameLayout { mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); mRunningOneHandedAnimator.start(); } else { - securityView.setTranslationX(targetTranslation); + mSecurityViewFlipper.setTranslationX(targetTranslation); } } - @Nullable - private KeyguardSecurityViewFlipper findKeyguardSecurityView() { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - - if (isKeyguardSecurityView(child)) { - return (KeyguardSecurityViewFlipper) child; - } - } - - return null; - } - - private boolean isKeyguardSecurityView(View view) { - return view instanceof KeyguardSecurityViewFlipper; - } - public void onPause() { if (mAlertDialog != null) { mAlertDialog.dismiss(); @@ -635,7 +596,7 @@ public class KeyguardSecurityContainer extends FrameLayout { for (int i = 0; i < getChildCount(); i++) { final View view = getChildAt(i); if (view.getVisibility() != GONE) { - if (mOneHandedMode && isKeyguardSecurityView(view)) { + if (mOneHandedMode && view == mSecurityViewFlipper) { measureChildWithMargins(view, halfWidthMeasureSpec, 0, heightMeasureSpec, 0); } else { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index fde8213de0c6..cb5c6c3a0090 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -32,6 +32,7 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.metrics.LogMaker; import android.os.UserHandle; +import android.provider.Settings; import android.util.Log; import android.util.Slog; import android.view.MotionEvent; @@ -49,6 +50,8 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; +import com.android.systemui.R; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -74,12 +77,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final SecurityCallback mSecurityCallback; private final ConfigurationController mConfigurationController; + private final FalsingCollector mFalsingCollector; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; - private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() { + @VisibleForTesting + final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() { private MotionEvent mTouchDown; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { @@ -91,6 +96,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard // Do just a bit of our own falsing. People should only be tapping on the input, not // swiping. if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { + // If we're in one handed mode, the user can tap on the opposite side of the screen + // to move the bouncer across. In that case, inhibit the falsing (otherwise the taps + // to move the bouncer to each screen side can end up closing it instead). + if (mView.isOneHandedMode()) { + if ((mView.isOneHandedModeLeftAligned() && ev.getX() > mView.getWidth() / 2f) + || (!mView.isOneHandedModeLeftAligned() + && ev.getX() <= mView.getWidth() / 2f)) { + mFalsingCollector.avoidGesture(); + } + } + if (mTouchDown != null) { mTouchDown.recycle(); mTouchDown = null; @@ -202,7 +218,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard KeyguardStateController keyguardStateController, SecurityCallback securityCallback, KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + FalsingCollector falsingCollector) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -216,6 +233,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardSecurityCallback); mConfigurationController = configurationController; mLastOrientation = getResources().getConfiguration().orientation; + mFalsingCollector = falsingCollector; } @Override @@ -440,13 +458,49 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (newView != null) { newView.onResume(KeyguardSecurityView.VIEW_REVEALED); mSecurityViewFlipperController.show(newView); - mView.updateLayoutForSecurityMode(securityMode); + configureOneHandedMode(); } mSecurityCallback.onSecurityModeChanged( securityMode, newView != null && newView.needsInput()); } + /** Read whether the one-handed keyguard should be on the left/right from settings. */ + private boolean isOneHandedKeyguardLeftAligned() { + try { + return Settings.Global.getInt(mView.getContext().getContentResolver(), + Settings.Global.ONE_HANDED_KEYGUARD_SIDE) + == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT; + } catch (Settings.SettingNotFoundException ex) { + return true; + } + } + + private boolean canUseOneHandedBouncer() { + // Is it enabled? + if (!getResources().getBoolean( + com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) { + return false; + } + + if (!KeyguardSecurityModel.isSecurityViewOneHanded(mCurrentSecurityMode)) { + return false; + } + + return getResources().getBoolean(R.bool.can_use_one_handed_bouncer); + } + + private void configureOneHandedMode() { + boolean oneHandedMode = canUseOneHandedBouncer(); + + mView.setOneHandedMode(oneHandedMode); + + if (oneHandedMode) { + mView.setOneHandedModeLeftAligned( + isOneHandedKeyguardLeftAligned(), /* animate= */false); + } + } + public void reportFailedUnlockAttempt(int userId, int timeoutMs) { // +1 for this time final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; @@ -511,13 +565,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard int newOrientation = getResources().getConfiguration().orientation; if (newOrientation != mLastOrientation) { mLastOrientation = newOrientation; - mView.updateLayoutForSecurityMode(mCurrentSecurityMode); + configureOneHandedMode(); } } /** Update keyguard position based on a tapped X coordinate. */ public void updateKeyguardPosition(float x) { - mView.updateKeyguardPosition(x); + if (mView.isOneHandedMode()) { + mView.setOneHandedModeLeftAligned(x <= mView.getWidth() / 2f, false); + } } static class Factory { @@ -533,6 +589,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final ConfigurationController mConfigurationController; + private final FalsingCollector mFalsingCollector; @Inject Factory(KeyguardSecurityContainer view, @@ -545,7 +602,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, KeyguardSecurityViewFlipperController securityViewFlipperController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + FalsingCollector falsingCollector) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; mLockPatternUtils = lockPatternUtils; @@ -556,6 +614,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardStateController = keyguardStateController; mSecurityViewFlipperController = securityViewFlipperController; mConfigurationController = configurationController; + mFalsingCollector = falsingCollector; } public KeyguardSecurityContainerController create( @@ -564,7 +623,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, securityCallback, mSecurityViewFlipperController, - mConfigurationController); + mConfigurationController, mFalsingCollector); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 2096c310744d..215840142f42 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -19,6 +19,7 @@ package com.android.keyguard; import android.graphics.Rect; import android.util.Slog; +import com.android.keyguard.KeyguardClockSwitch.ClockSize; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController; import com.android.systemui.statusbar.notification.AnimatableProperty; @@ -126,10 +127,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** - * Set whether or not the lock screen is showing notifications. + * Set which clock should be displayed on the keyguard. The other one will be automatically + * hidden. */ - public void setHasVisibleNotifications(boolean hasVisibleNotifications) { - mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications); + public void displayClock(@ClockSize int clockSize) { + mKeyguardClockSwitchController.displayClock(clockSize); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index bd000b2effa3..1344be62e059 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2785,6 +2785,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateBiometricListeningState(); } + /** Notifies that the occluded state changed. */ + public void onKeyguardOccludedChanged(boolean occluded) { + Assert.isMainThread(); + if (DEBUG) { + Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")"); + } + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onKeyguardOccludedChanged(occluded); + } + } + } + /** * Handle {@link #MSG_KEYGUARD_RESET} */ @@ -2967,6 +2981,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab callback.onPhoneStateChanged(mPhoneState); callback.onRefreshCarrierInfo(); callback.onClockVisibilityChanged(); + callback.onKeyguardOccludedChanged(mKeyguardOccluded); callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible); callback.onTelephonyCapable(mTelephonyCapable); callback.onLockScreenModeChanged(mLockScreenMode); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 9849a7efe837..6aa7aaa4d488 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -88,6 +88,12 @@ public class KeyguardUpdateMonitorCallback { */ public void onKeyguardVisibilityChanged(boolean showing) { } + /** + * Called when the keyguard occluded state changes. + * @param occluded Indicates if the keyguard is now occluded. + */ + public void onKeyguardOccludedChanged(boolean occluded) { } + public void onKeyguardVisibilityChangedRaw(boolean showing) { final long now = SystemClock.elapsedRealtime(); if (showing == mShowing diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index ca2c034c5d32..fa56453a8992 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -161,7 +161,7 @@ public class SystemActions extends SystemUI { mNotificationShadeController = notificationShadeController; // Saving in instance variable since to prevent GC since // NotificationShadeWindowController.registerCallback() only keeps weak references. - mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) -> + mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) -> registerOrUnregisterDismissNotificationShadeAction(); mStatusBar = statusBar; } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 717c7156f917..a51e3fcfd50b 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -28,6 +28,7 @@ import android.annotation.Nullable; import android.annotation.UiContext; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Matrix; @@ -35,6 +36,7 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; @@ -76,6 +78,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener { private static final String TAG = "WindowMagnificationController"; + @SuppressWarnings("isloggabletaglength") + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Build.IS_DEBUGGABLE; // Delay to avoid updating state description too frequently. private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100; // It should be consistent with the value defined in WindowMagnificationGestureHandler. @@ -158,14 +162,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mRotation = display.getRotation(); mWm = context.getSystemService(WindowManager.class); - mWindowBounds = mWm.getCurrentWindowMetrics().getBounds(); + mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds()); mResources = mContext.getResources(); mScale = mResources.getInteger(R.integer.magnification_default_scale); mBounceEffectDuration = mResources.getInteger( com.android.internal.R.integer.config_shortAnimTime); updateDimensions(); - setInitialStartBounds(); + setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2, + mWindowBounds.height() / 2); computeBounceAnimationScale(); mMirrorWindowControl = mirrorWindowControl; @@ -286,18 +291,59 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold * @param configDiff a bit mask of the differences between the configurations */ void onConfigurationChanged(int configDiff) { + if (DEBUG) { + Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString( + configDiff)); + } + if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { + onRotate(); + } + + if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) { + updateAccessibilityWindowTitleIfNeeded(); + } + + boolean reCreateWindow = false; if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) { updateDimensions(); computeBounceAnimationScale(); - if (isWindowVisible()) { - deleteWindowMagnification(); - enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); + reCreateWindow = true; + } + + if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) { + reCreateWindow |= handleScreenSizeChanged(); + } + + // Recreate the window again to correct the window appearance due to density or + // window size changed not caused by rotation. + if (isWindowVisible() && reCreateWindow) { + deleteWindowMagnification(); + enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); + } + } + + /** + * Calculates the magnification frame if the window bounds is changed. + * Note that the orientation also changes the wind bounds, so it should be handled first. + * + * @return {@code true} if the magnification frame is changed with the new window bounds. + */ + private boolean handleScreenSizeChanged() { + final Rect oldWindowBounds = new Rect(mWindowBounds); + final Rect currentWindowBounds = mWm.getCurrentWindowMetrics().getBounds(); + + if (currentWindowBounds.equals(oldWindowBounds)) { + if (DEBUG) { + Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed"); } - } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { - onRotate(); - } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) { - updateAccessibilityWindowTitleIfNeeded(); + return false; } + mWindowBounds.set(currentWindowBounds); + final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); + final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); + setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY); + calculateMagnificationFrameBoundary(); + return true; } private void updateSystemUIStateIfNeeded() { @@ -311,30 +357,42 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mWm.updateViewLayout(mMirrorView, params); } - /** Handles MirrorWindow position when the device rotation changed. */ + /** + * Keep MirrorWindow position on the screen unchanged when device rotates 90° clockwise or + * anti-clockwise. + */ private void onRotate() { final Display display = mContext.getDisplay(); final int oldRotation = mRotation; - mWindowBounds = mWm.getCurrentWindowMetrics().getBounds(); - - setMagnificationFrameBoundary(); mRotation = display.getRotation(); + final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation); + if (rotationDegree == 0 || rotationDegree == 180) { + Log.w(TAG, "onRotate -- rotate with the device. skip it"); + return; + } + final Rect currentWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds()); + if (currentWindowBounds.width() != mWindowBounds.height() + || currentWindowBounds.height() != mWindowBounds.width()) { + Log.w(TAG, "onRotate -- unexpected window height/width"); + return; + } + + mWindowBounds.set(currentWindowBounds); + + calculateMagnificationFrameBoundary(); if (!isWindowVisible()) { return; } // Keep MirrorWindow position on the screen unchanged when device rotates 90° // clockwise or anti-clockwise. - final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation); + final Matrix matrix = new Matrix(); matrix.setRotate(rotationDegree); if (rotationDegree == 90) { matrix.postTranslate(mWindowBounds.width(), 0); } else if (rotationDegree == 270) { matrix.postTranslate(0, mWindowBounds.height()); - } else { - Log.w(TAG, "Invalid rotation change. " + rotationDegree); - return; } // The rect of MirrorView is going to be transformed. LayoutParams params = @@ -440,12 +498,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold } } - private void setInitialStartBounds() { + private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) { // Sets the initial frame area for the mirror and places it in the center of the display. - final int initSize = Math.min(mWindowBounds.width(), mWindowBounds.height()) / 2 + final int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2 + 2 * mMirrorSurfaceMargin; - final int initX = mWindowBounds.width() / 2 - initSize / 2; - final int initY = mWindowBounds.height() / 2 - initSize / 2; + final int initX = centerX - initSize / 2; + final int initY = centerY - initSize / 2; mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize); } @@ -553,7 +611,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mSourceBounds.set(left, top, right, bottom); } - private void setMagnificationFrameBoundary() { + private void calculateMagnificationFrameBoundary() { // Calculates width and height for magnification frame could exceed out the screen. // TODO : re-calculating again when scale is changed. // The half width of magnification frame. @@ -644,7 +702,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold : centerY - mMagnificationFrame.exactCenterY(); mScale = Float.isNaN(scale) ? mScale : scale; - setMagnificationFrameBoundary(); + calculateMagnificationFrameBoundary(); updateMagnificationFramePosition((int) offsetX, (int) offsetY); if (!isWindowVisible()) { createMirrorWindow(); @@ -764,6 +822,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets); pw.println(" mScale:" + mScale); pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty")); + pw.println(" mSourceBounds:" + + (isWindowVisible() ? mSourceBounds : "empty")); pw.println(" mSystemGestureTop:" + mSystemGestureTop); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index c97a30e6e13e..83fa994d2f05 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -60,9 +60,11 @@ import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarA11yHelper; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarOverlayController; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.ReduceBrightColorsController; @@ -234,6 +236,8 @@ public class DependencyProvider { UiEventLogger uiEventLogger, NavigationBarOverlayController navBarOverlayController, ConfigurationController configurationController, + NavigationBarA11yHelper navigationBarA11yHelper, + TaskbarDelegate taskbarDelegate, UserTracker userTracker) { return new NavigationBarController(context, windowManager, @@ -261,6 +265,8 @@ public class DependencyProvider { uiEventLogger, navBarOverlayController, configurationController, + navigationBarA11yHelper, + taskbarDelegate, userTracker); } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index bc4ced452630..98fb3c9b9107 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -199,7 +199,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite iWindowManager, backgroundExecutor, uiEventLogger, - null, ringerModeTracker, sysUiState, handler, @@ -338,8 +337,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions, adapter, overflowAdapter, sysuiColorExtractor, statusBarService, notificationShadeWindowController, sysuiState, onRotateCallback, - keyguardShowing, powerAdapter, uiEventLogger, null, - statusBar); + keyguardShowing, powerAdapter, uiEventLogger, statusBar); mWalletFactory = walletFactory; // Update window attributes diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 5acb3038b91b..2afce719c406 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -190,7 +190,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final MetricsLogger mMetricsLogger; private final UiEventLogger mUiEventLogger; private final SysUiState mSysUiState; - private final GlobalActionsInfoProvider mInfoProvider; // Used for RingerModeTracker private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); @@ -333,10 +332,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene IWindowManager iWindowManager, @Background Executor backgroundExecutor, UiEventLogger uiEventLogger, - GlobalActionsInfoProvider infoProvider, RingerModeTracker ringerModeTracker, SysUiState sysUiState, - @Main Handler handler, + @Main Handler handler, PackageManager packageManager, StatusBar statusBar) { mContext = context; @@ -358,7 +356,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mTelecomManager = telecomManager; mMetricsLogger = metricsLogger; mUiEventLogger = uiEventLogger; - mInfoProvider = infoProvider; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -653,7 +650,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger, - mInfoProvider, mStatusBar); + mStatusBar); dialog.setOnDismissListener(this); dialog.setOnShowListener(this); @@ -2124,7 +2121,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private Dialog mPowerOptionsDialog; protected final Runnable mOnRotateCallback; private UiEventLogger mUiEventLogger; - private GlobalActionsInfoProvider mInfoProvider; private GestureDetector mGestureDetector; private StatusBar mStatusBar; @@ -2178,7 +2174,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, - @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) { + StatusBar statusBar) { super(context, themeRes); mContext = context; mAdapter = adapter; @@ -2191,7 +2187,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mOnRotateCallback = onRotateCallback; mKeyguardShowing = keyguardShowing; mUiEventLogger = uiEventLogger; - mInfoProvider = infoProvider; mStatusBar = statusBar; mGestureDetector = new GestureDetector(mContext, mGestureListener); @@ -2309,10 +2304,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mBackgroundDrawable = new ScrimDrawable(); mScrimAlpha = 1.0f; } - - if (mInfoProvider != null && mInfoProvider.shouldShowMessage()) { - mInfoProvider.addPanel(mContext, mContainer, mAdapter.getCount(), () -> dismiss()); - } } protected void fixNavBarClipping() { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt deleted file mode 100644 index 25837e3aacdf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2021 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.globalactions - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.content.res.Configuration -import android.net.Uri -import android.service.quickaccesswallet.QuickAccessWalletClient -import android.util.Log -import android.view.LayoutInflater -import android.view.ViewGroup -import android.widget.TextView -import com.android.systemui.R -import com.android.systemui.controls.controller.ControlsController -import com.android.systemui.plugins.ActivityStarter -import javax.inject.Inject - -private const val TAG = "GlobalActionsInfo" - -/** Maximum number of times to show change info message */ -private const val MAX_VIEW_COUNT = 3 - -/** Maximum number of buttons allowed in landscape before this panel does not fit */ -private const val MAX_BUTTONS_LANDSCAPE = 4 - -private const val PREFERENCE = "global_actions_info_prefs" -private const val KEY_VIEW_COUNT = "view_count" - -class GlobalActionsInfoProvider @Inject constructor( - private val context: Context, - private val walletClient: QuickAccessWalletClient, - private val controlsController: ControlsController, - private val activityStarter: ActivityStarter -) { - - private var pendingIntent: PendingIntent - - init { - val url = context.resources.getString(R.string.global_actions_change_url) - val intent = Intent(Intent.ACTION_VIEW).apply { - setData(Uri.parse(url)) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) - } - - fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) { - // This panel does not fit on landscape with two rows of buttons showing, - // so skip adding the panel (and incrementing view counT) in that case - val isLandscape = - context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE - if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) { - return - } - - val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel, - parent, false) - - val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title) - val message = view.findViewById<TextView>(R.id.global_actions_change_message) - message?.setText(context.getString(R.string.global_actions_change_description, walletTitle)) - - view.setOnClickListener { _ -> - dismissParent.run() - activityStarter.postStartActivityDismissingKeyguard(pendingIntent) - } - parent.addView(view, 0) // Add to top - incrementViewCount() - } - - fun shouldShowMessage(): Boolean { - // This is only relevant for some devices - val isEligible = context.resources.getBoolean( - R.bool.global_actions_show_change_info) - if (!isEligible) { - return false - } - - val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) - - // Only show to users who previously had these items set up - val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) { - sharedPrefs.getInt(KEY_VIEW_COUNT, 0) - } else { - -1 - } - - // Limit number of times this is displayed - return viewCount > -1 && viewCount < MAX_VIEW_COUNT - } - - private fun hadContent(): Boolean { - // Check whether user would have seen content in the power menu that has now moved - val hadControls = controlsController.getFavorites().size > 0 - val hadCards = walletClient.isWalletFeatureAvailable - Log.d(TAG, "Previously had controls $hadControls, cards $hadCards") - return hadControls || hadCards - } - - private fun incrementViewCount() { - val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE) - val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0) - sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 26f38ddd5919..7f42530916cf 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -23,10 +23,12 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.containsType; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS; @@ -44,6 +46,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; @@ -54,9 +57,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE; import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions; -import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.IdRes; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; @@ -68,6 +69,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.database.ContentObserver; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; @@ -101,7 +103,6 @@ import android.view.WindowInsetsController.Behavior; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; import android.view.inputmethod.InputMethodManager; import androidx.annotation.VisibleForTesting; @@ -130,6 +131,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.AutoHideUiElement; @@ -151,7 +153,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; import java.io.PrintWriter; -import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.function.Consumer; @@ -162,8 +163,7 @@ import dagger.Lazy; * Contains logic for a navigation bar view. */ public class NavigationBar implements View.OnAttachStateChangeListener, - Callbacks, NavigationModeController.ModeChangedListener, - AccessibilityButtonModeObserver.ModeChangedListener { + Callbacks, NavigationModeController.ModeChangedListener { public static final String TAG = "NavigationBar"; private static final boolean DEBUG = false; @@ -180,7 +180,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Context mContext; private final WindowManager mWindowManager; private final AccessibilityManager mAccessibilityManager; - private final AccessibilityManagerWrapper mAccessibilityManagerWrapper; private final DeviceProvisionedController mDeviceProvisionedController; private final StatusBarStateController mStatusBarStateController; private final MetricsLogger mMetricsLogger; @@ -201,11 +200,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final Handler mHandler; private final NavigationBarOverlayController mNavbarOverlayController; private final UiEventLogger mUiEventLogger; + private final NavigationBarA11yHelper mNavigationBarA11yHelper; private final UserTracker mUserTracker; private final NotificationShadeDepthController mNotificationShadeDepthController; private Bundle mSavedState; private NavigationBarView mNavigationBarView; + private NavigationBarFrame mFrame; private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; @@ -483,11 +484,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, UiEventLogger uiEventLogger, + NavigationBarA11yHelper navigationBarA11yHelper, UserTracker userTracker) { mContext = context; mWindowManager = windowManager; mAccessibilityManager = accessibilityManager; - mAccessibilityManagerWrapper = accessibilityManagerWrapper; mDeviceProvisionedController = deviceProvisionedController; mStatusBarStateController = statusBarStateController; mMetricsLogger = metricsLogger; @@ -508,11 +509,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; mUiEventLogger = uiEventLogger; + mNavigationBarA11yHelper = navigationBarA11yHelper; mUserTracker = userTracker; mNotificationShadeDepthController = notificationShadeDepthController; mNavBarMode = mNavigationModeController.addListener(this); - mAccessibilityButtonModeObserver.addListener(this); } public NavigationBarView getView() { @@ -520,34 +521,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } public View createView(Bundle savedState) { - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - lp.token = new Binder(); - lp.accessibilityTitle = mContext.getString(R.string.nav_bar); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; - lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - lp.windowAnimations = 0; - lp.setTitle("NavigationBar" + mContext.getDisplayId()); - lp.setFitInsetsTypes(0 /* types */); - lp.setTrustedOverlay(); - - NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate( + mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate( R.layout.navigation_bar_window, null); - View barView = LayoutInflater.from(frame.getContext()).inflate( - R.layout.navigation_bar, frame); + View barView = LayoutInflater.from(mFrame.getContext()).inflate( + R.layout.navigation_bar, mFrame); barView.addOnAttachStateChangeListener(this); mNavigationBarView = barView.findViewById(R.id.navigation_bar_view); if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView); - mContext.getSystemService(WindowManager.class).addView(frame, lp); + mContext.getSystemService(WindowManager.class).addView(mFrame, + getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration + .getRotation())); mDisplayId = mContext.getDisplayId(); mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; @@ -605,9 +589,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mContext.getSystemService(WindowManager.class).removeViewImmediate( mNavigationBarView.getRootView()); mNavigationModeController.removeListener(this); - mAccessibilityButtonModeObserver.removeListener(this); - mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); + mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener); mContentResolver.unregisterContentObserver(mAssistContentObserver); mDeviceProvisionedController.removeCallback(mUserSetupListener); mNotificationShadeDepthController.removeListener(mDepthListener); @@ -629,7 +612,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.setWindowVisible(isNavBarWindowVisible()); mNavigationBarView.setBehavior(mBehavior); - mAccessibilityManagerWrapper.addCallback(mAccessibilityListener); + mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener); mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener); mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener); @@ -704,6 +687,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler.removeCallbacks(mAutoDim); mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mHandler.removeCallbacks(mEnableLayoutTransitions); + mFrame = null; mNavigationBarView = null; mOrientationHandle = null; } @@ -722,6 +706,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, * Called when a non-reloading configuration change happens and we need to update. */ public void onConfigurationChanged(Configuration newConfig) { + final int rotation = newConfig.windowConfiguration.getRotation(); final Locale locale = mContext.getResources().getConfiguration().locale; final int ld = TextUtils.getLayoutDirectionFromLocale(locale); if (!locale.equals(mLocale) || ld != mLayoutDirection) { @@ -735,9 +720,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, refreshLayout(ld); } - repositionNavigationBar(); + repositionNavigationBar(rotation); if (canShowSecondaryHandle()) { - int rotation = newConfig.windowConfiguration.getRotation(); if (rotation != mCurrentRotation) { mCurrentRotation = rotation; orientSecondaryHomeHandle(); @@ -889,26 +873,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return; } boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; - int hints = mNavigationIconHints; - switch (backDisposition) { - case InputMethodService.BACK_DISPOSITION_DEFAULT: - case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS: - case InputMethodService.BACK_DISPOSITION_WILL_DISMISS: - if (imeShown) { - hints |= NAVIGATION_HINT_BACK_ALT; - } else { - hints &= ~NAVIGATION_HINT_BACK_ALT; - } - break; - case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING: - hints &= ~NAVIGATION_HINT_BACK_ALT; - break; - } - if (showImeSwitcher) { - hints |= NAVIGATION_HINT_IME_SHOWN; - } else { - hints &= ~NAVIGATION_HINT_IME_SHOWN; - } + int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition, + imeShown, showImeSwitcher); if (hints == mNavigationIconHints) return; mNavigationIconHints = hints; @@ -1120,13 +1086,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; } - private void repositionNavigationBar() { - if (!mNavigationBarView.isAttachedToWindow()) return; + private void repositionNavigationBar(int rotation) { + if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; prepareNavigationBarView(); - mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(), - ((View) mNavigationBarView.getParent()).getLayoutParams()); + mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation)); } private void updateScreenPinningGestures() { @@ -1168,7 +1133,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); - updateAccessibilityServicesState(mAccessibilityManager); + updateAccessibilityServicesState(); ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton(); imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick); @@ -1389,9 +1354,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, return true; } - void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) { - boolean[] feedbackEnabled = new boolean[1]; - int a11yFlags = getA11yButtonState(feedbackEnabled); + void updateAccessibilityServicesState() { + int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; @@ -1410,7 +1374,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, public void updateSystemUiStateFlags(int a11yFlags) { if (a11yFlags < 0) { - a11yFlags = getA11yButtonState(null); + a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); } boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; @@ -1420,6 +1384,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, .setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible()) .setFlag(SYSUI_STATE_IME_SHOWING, (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) + .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, + (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) .setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY, allowSystemGestureIgnoringBarVisibility()) .commitUpdate(mDisplayId); @@ -1435,45 +1401,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } - /** - * Returns the system UI flags corresponding the the current accessibility button state - * - * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled. - */ - public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) { - boolean feedbackEnabled = false; - // AccessibilityManagerService resolves services for the current user since the local - // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission - final List<AccessibilityServiceInfo> services = - mAccessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_ALL_MASK); - final List<String> a11yButtonTargets = - mAccessibilityManager.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON); - final int requestingServices = a11yButtonTargets.size(); - for (int i = services.size() - 1; i >= 0; --i) { - AccessibilityServiceInfo info = services.get(i); - if (info.feedbackType != 0 && info.feedbackType != - AccessibilityServiceInfo.FEEDBACK_GENERIC) { - feedbackEnabled = true; - } - } - - if (outFeedbackEnabled != null) { - outFeedbackEnabled[0] = feedbackEnabled; - } - - // If accessibility button is floating menu mode, click and long click state should be - // disabled. - if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode() - == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { - return 0; - } - - return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0) - | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0); - } - private void updateAssistantEntrypoints() { mAssistantAvailable = mAssistManagerLazy.get() .getAssistInfoForUser(UserHandle.USER_CURRENT) != null; @@ -1575,11 +1502,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } } - @Override - public void onAccessibilityButtonModeChanged(int mode) { - updateAccessibilityServicesState(mAccessibilityManager); - } - public void disableAnimationsDuringHide(long delay) { mNavigationBarView.setLayoutTransitionsEnabled(false); mHandler.postDelayed(mEnableLayoutTransitions, @@ -1604,22 +1526,110 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mNavigationBarView.getBarTransitions().finishAnimations(); } - private final AccessibilityServicesStateChangeListener mAccessibilityListener = + private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener = this::updateAccessibilityServicesState; + private WindowManager.LayoutParams getBarLayoutParams(int rotation) { + WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); + lp.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot); + } + return lp; + } + + private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { + int width = WindowManager.LayoutParams.MATCH_PARENT; + int height = WindowManager.LayoutParams.MATCH_PARENT; + int insetsHeight = -1; + int gravity = Gravity.BOTTOM; + if (INSETS_LAYOUT_GENERALIZATION) { + boolean navBarCanMove = true; + if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) { + Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds(); + navBarCanMove = displaySize.width() != displaySize.height() + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_navBarCanMove); + } + if (!navBarCanMove) { + height = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_frame_height); + insetsHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height); + } else { + switch (rotation) { + case ROTATION_UNDEFINED: + case Surface.ROTATION_0: + case Surface.ROTATION_180: + height = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_frame_height); + insetsHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height); + break; + case Surface.ROTATION_90: + gravity = Gravity.RIGHT; + width = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_width); + break; + case Surface.ROTATION_270: + gravity = Gravity.LEFT; + width = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_width); + break; + } + } + } + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + width, + height, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + if (INSETS_LAYOUT_GENERALIZATION) { + lp.gravity = gravity; + if (insetsHeight != -1) { + lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0); + } else { + lp.providedInternalInsets = Insets.NONE; + } + } + lp.token = new Binder(); + lp.accessibilityTitle = mContext.getString(R.string.nav_bar); + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + lp.windowAnimations = 0; + lp.setTitle("NavigationBar" + mContext.getDisplayId()); + lp.setFitInsetsTypes(0 /* types */); + lp.setTrustedOverlay(); + return lp; + } + private boolean canShowSecondaryHandle() { return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null; } private final Consumer<Integer> mRotationWatcher = rotation -> { - if (mNavigationBarView.needsReorient(rotation)) { - repositionNavigationBar(); + if (mNavigationBarView != null + && mNavigationBarView.needsReorient(rotation)) { + repositionNavigationBar(rotation); } }; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + // This receiver is unregistered when the view is detached, but on devices with multiple + // displays, it can sometimes still receive an ACTION_SCREEN_ON/ACTION_SCREEN_OFF on + // display switch, after it was detached, so this null check ensures no crash in that + // scenario. + if (mNavigationBarView == null) { + return; + } String action = intent.getAction(); if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { @@ -1628,7 +1638,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } if (Intent.ACTION_USER_SWITCHED.equals(action)) { // The accessibility settings may be different for the new user - updateAccessibilityServicesState(mAccessibilityManager); + updateAccessibilityServicesState(); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java new file mode 100644 index 000000000000..13e6d8b410d6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java @@ -0,0 +1,90 @@ +package com.android.systemui.navigationbar; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; + +import android.view.accessibility.AccessibilityManager; + +import com.android.systemui.accessibility.AccessibilityButtonModeObserver; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +/** + * Extracts shared elements of a11y necessary between navbar and taskbar delegate + */ +@SysUISingleton +public final class NavigationBarA11yHelper implements + AccessibilityButtonModeObserver.ModeChangedListener { + private final AccessibilityManager mAccessibilityManager; + private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver; + private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>(); + + @Inject + public NavigationBarA11yHelper(AccessibilityManager accessibilityManager, + AccessibilityManagerWrapper accessibilityManagerWrapper, + AccessibilityButtonModeObserver accessibilityButtonModeObserver) { + mAccessibilityManager = accessibilityManager; + accessibilityManagerWrapper.addCallback( + accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate()); + mAccessibilityButtonModeObserver = accessibilityButtonModeObserver; + + mAccessibilityButtonModeObserver.addListener(this); + } + + public void registerA11yEventListener(NavA11yEventListener listener) { + mA11yEventListeners.add(listener); + } + + public void removeA11yEventListener(NavA11yEventListener listener) { + mA11yEventListeners.remove(listener); + } + + private void dispatchEventUpdate() { + for (NavA11yEventListener listener : mA11yEventListeners) { + listener.updateAccessibilityServicesState(); + } + } + + @Override + public void onAccessibilityButtonModeChanged(int mode) { + dispatchEventUpdate(); + } + + /** + * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and + * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE} + * + * @return the a11y button clickable and long_clickable states, or 0 if there is no + * a11y button in the navbar + */ + public int getA11yButtonState() { + // AccessibilityManagerService resolves services for the current user since the local + // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission + final List<String> a11yButtonTargets = + mAccessibilityManager.getAccessibilityShortcutTargets( + AccessibilityManager.ACCESSIBILITY_BUTTON); + final int requestingServices = a11yButtonTargets.size(); + + // If accessibility button is floating menu mode, click and long click state should be + // disabled. + if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode() + == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) { + return 0; + } + + return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0) + | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0); + } + + public interface NavA11yEventListener { + void updateAccessibilityServicesState(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index b9e9240b354a..aa5964bf5d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -27,7 +27,6 @@ import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.os.SystemProperties; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; @@ -113,6 +112,7 @@ public class NavigationBarController implements Callbacks, private final SystemActions mSystemActions; private final UiEventLogger mUiEventLogger; private final Handler mHandler; + private final NavigationBarA11yHelper mNavigationBarA11yHelper; private final DisplayManager mDisplayManager; private final NavigationBarOverlayController mNavBarOverlayController; private final TaskbarDelegate mTaskbarDelegate; @@ -157,6 +157,8 @@ public class NavigationBarController implements Callbacks, UiEventLogger uiEventLogger, NavigationBarOverlayController navBarOverlayController, ConfigurationController configurationController, + NavigationBarA11yHelper navigationBarA11yHelper, + TaskbarDelegate taskbarDelegate, UserTracker userTracker) { mContext = context; mWindowManager = windowManager; @@ -182,6 +184,7 @@ public class NavigationBarController implements Callbacks, mSystemActions = systemActions; mUiEventLogger = uiEventLogger; mHandler = mainHandler; + mNavigationBarA11yHelper = navigationBarA11yHelper; mDisplayManager = mContext.getSystemService(DisplayManager.class); commandQueue.addCallback(this); configurationController.addCallback(this); @@ -189,7 +192,9 @@ public class NavigationBarController implements Callbacks, mNavBarOverlayController = navBarOverlayController; mNavMode = mNavigationModeController.addListener(this); mNavigationModeController.addListener(this); - mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService); + mTaskbarDelegate = taskbarDelegate; + mTaskbarDelegate.setOverviewProxyService(overviewProxyService, + navigationBarA11yHelper, mSysUiFlagsContainer); mIsTablet = isTablet(mContext.getResources().getConfiguration()); mUserTracker = userTracker; } @@ -237,25 +242,28 @@ public class NavigationBarController implements Callbacks, }); } - /** - * @return {@code true} if navbar was added/removed, false otherwise - */ - public boolean updateNavbarForTaskbar() { - if (!isThreeButtonTaskbarFlagEnabled()) { - return false; + /** @see #initializeTaskbarIfNecessary() */ + private boolean updateNavbarForTaskbar() { + boolean taskbarShown = initializeTaskbarIfNecessary(); + if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) { + createNavigationBar(mContext.getDisplay(), null, null); } + return taskbarShown; + } - if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) { + /** @return {@code true} if taskbar is enabled, false otherwise */ + private boolean initializeTaskbarIfNecessary() { + boolean isShowingTaskbar = mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON; + if (isShowingTaskbar) { // Remove navigation bar when taskbar is showing, currently only for 3 button mode removeNavigationBar(mContext.getDisplayId()); mCommandQueue.addCallback(mTaskbarDelegate); - } else if (mNavigationBars.get(mContext.getDisplayId()) == null) { - // Add navigation bar after taskbar goes away - createNavigationBar(mContext.getDisplay(), null, null); + mTaskbarDelegate.init(mContext.getDisplayId()); + } else { mCommandQueue.removeCallback(mTaskbarDelegate); + mTaskbarDelegate.destroy(); } - - return true; + return isShowingTaskbar; } @Override @@ -302,7 +310,7 @@ public class NavigationBarController implements Callbacks, */ public void createNavigationBars(final boolean includeDefaultDisplay, RegisterStatusBarResult result) { - if (updateNavbarForTaskbar()) { + if (initializeTaskbarIfNecessary()) { return; } @@ -326,7 +334,7 @@ public class NavigationBarController implements Callbacks, return; } - if (isThreeButtonTaskbarEnabled()) { + if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) { return; } @@ -371,6 +379,7 @@ public class NavigationBarController implements Callbacks, mHandler, mNavBarOverlayController, mUiEventLogger, + mNavigationBarA11yHelper, mUserTracker); mNavigationBars.put(displayId, navBar); @@ -462,15 +471,6 @@ public class NavigationBarController implements Callbacks, return mNavigationBars.get(DEFAULT_DISPLAY); } - private boolean isThreeButtonTaskbarEnabled() { - return mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON && - isThreeButtonTaskbarFlagEnabled(); - } - - private boolean isThreeButtonTaskbarFlagEnabled() { - return SystemProperties.getBoolean("persist.debug.taskbar_three_button", false); - } - private boolean isTablet(Configuration newConfig) { float density = Resources.getSystem().getDisplayMetrics().density; int size = Math.min((int) (density * newConfig.screenWidthDp), diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java index 649ac875b4c2..a5b7911d3b3d 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java @@ -47,6 +47,8 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; +import com.android.systemui.shared.recents.utilities.Utilities; +import com.android.systemui.shared.recents.utilities.ViewRippler; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; @@ -311,7 +313,7 @@ public class RotationButtonController { // Prepare to show the navbar icon by updating the icon style to change anim params mLastRotationSuggestion = rotation; // Remember rotation for click - final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation); + final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation); if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) { mIconResId = rotationCCW ? R.drawable.ic_sysbar_rotate_button_ccw_start_90 @@ -431,23 +433,6 @@ public class RotationButtonController { return rotation == NATURAL_ROTATION; } - private boolean isRotationAnimationCCW(int from, int to) { - // All 180deg WM rotation animations are CCW, match that - if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false; - if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW - if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true; - if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true; - if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false; - if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW - if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW - if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true; - if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false; - if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false; - if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW - if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true; - return false; // Default - } - private void rescheduleRotationTimeout(final boolean reasonHover) { // May be called due to a new rotation proposal or a change in hover state if (reasonHover) { @@ -520,38 +505,6 @@ public class RotationButtonController { } } - private class ViewRippler { - private static final int RIPPLE_OFFSET_MS = 50; - private static final int RIPPLE_INTERVAL_MS = 2000; - private View mRoot; - - public void start(View root) { - stop(); // Stop any pending ripple animations - - mRoot = root; - - // Schedule pending ripples, offset the 1st to avoid problems with visibility change - mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS); - mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS); - mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS); - mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS); - mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS); - } - - public void stop() { - if (mRoot != null) mRoot.removeCallbacks(mRipple); - } - - private final Runnable mRipple = new Runnable() { - @Override - public void run() { // Cause the ripple to fire via false presses - if (!mRoot.isAttachedToWindow()) return; - mRoot.setPressed(true /* pressed */); - mRoot.setPressed(false /* pressed */); - } - }; - } - enum RotationButtonEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "The rotation button was shown") ROTATION_SUGGESTION_SHOWN(206), diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index 03147d8f1085..40afed30f4e2 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -16,23 +16,99 @@ package com.android.systemui.navigationbar; +import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; +import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; + +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING; + +import android.inputmethodservice.InputMethodService; import android.os.IBinder; +import com.android.internal.view.AppearanceRegion; +import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.statusbar.CommandQueue; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton public class TaskbarDelegate implements CommandQueue.Callbacks { - private final OverviewProxyService mOverviewProxyService; + private OverviewProxyService mOverviewProxyService; + private NavigationBarA11yHelper mNavigationBarA11yHelper; + private SysUiState mSysUiState; + private int mDisplayId; + private int mNavigationIconHints; + private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener = + this::updateSysuiFlags; + @Inject + public TaskbarDelegate() { /* no-op */ } - public TaskbarDelegate(OverviewProxyService overviewProxyService) { + public void setOverviewProxyService(OverviewProxyService overviewProxyService, + NavigationBarA11yHelper navigationBarA11yHelper, + SysUiState sysUiState) { + // TODO: adding this in the ctor results in a dagger dependency cycle :( mOverviewProxyService = overviewProxyService; + mNavigationBarA11yHelper = navigationBarA11yHelper; + mSysUiState = sysUiState; + } + + public void destroy() { + mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener); + } + + public void init(int displayId) { + mDisplayId = displayId; + mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener); + // Set initial state for any listeners + updateSysuiFlags(); + } + + private void updateSysuiFlags() { + int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState(); + boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0; + boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0; + + mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable) + .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable) + .setFlag(SYSUI_STATE_IME_SHOWING, + (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0) + .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING, + (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0) + .commitUpdate(mDisplayId); } @Override public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { - mOverviewProxyService.notifyImeWindowStatus(displayId, token, vis, backDisposition, - showImeSwitcher); + boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; + int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition, + imeShown, showImeSwitcher); + if (hints != mNavigationIconHints) { + mNavigationIconHints = hints; + updateSysuiFlags(); + } + } + + @Override + public void onRotationProposal(int rotation, boolean isValid) { + mOverviewProxyService.onRotationProposal(rotation, isValid); + } + + @Override + public void disable(int displayId, int state1, int state2, boolean animate) { + mOverviewProxyService.disable(displayId, state1, state2, animate); + } + + @Override + public void onSystemBarAttributesChanged(int displayId, int appearance, + AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior, + boolean isFullscreen) { + mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index cb0c411b2753..53259435da02 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -34,6 +34,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUP import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; @@ -736,12 +737,13 @@ public class OverviewProxyService extends CurrentUserTracker implements } private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, - boolean bouncerShowing) { + boolean bouncerShowing, boolean isDozing) { mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, keyguardShowing && !keyguardOccluded) .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED, keyguardShowing && keyguardOccluded) .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing) + .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing) .commitUpdate(mContext.getDisplayId()); } @@ -967,19 +969,40 @@ public class OverviewProxyService extends CurrentUserTracker implements } } - public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { + public void disable(int displayId, int state1, int state2, boolean animate) { try { if (mOverviewProxy != null) { - mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition, - showImeSwitcher); + mOverviewProxy.disable(displayId, state1, state2, animate); } else { - Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status."); + Log.e(TAG_OPS, "Failed to get overview proxy for disable flags."); } } catch (RemoteException e) { - Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e); + Log.e(TAG_OPS, "Failed to call disable()", e); } + } + public void onRotationProposal(int rotation, boolean isValid) { + try { + if (mOverviewProxy != null) { + mOverviewProxy.onRotationProposal(rotation, isValid); + } else { + Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation."); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onRotationProposal()", e); + } + } + + public void onSystemBarAttributesChanged(int displayId, int behavior) { + try { + if (mOverviewProxy != null) { + mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior); + } else { + Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change."); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e); + } } private void updateEnabledState() { @@ -1030,7 +1053,5 @@ public class OverviewProxyService extends CurrentUserTracker implements default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {} default void onAssistantGestureCompletion(float velocity) {} default void startAssistant(Bundle bundle) {} - default void onImeWindowStatusChanged(int displayId, IBinder token, int vis, - int backDisposition, boolean showImeSwitcher) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ead87a4c2f49..db734aa8865d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5643,6 +5643,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mSwipeHelper.resetExposedMenuView(animate, force); } + boolean isUsingSplitNotificationShade() { + return mShouldUseSplitNotificationShade; + } + static boolean matchesSelection( ExpandableNotificationRow row, @SelectedRows int selection) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 09afedb6de59..a92682a76a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1116,11 +1116,16 @@ public class NotificationStackScrollLayoutController { /** * Update whether we should show the empty shade view (no notifications in the shade). * If so, send the update to our view. + * + * When in split mode, notifications are always visible regardless of the state of the + * QuickSettings panel. That being the case, empty view is always shown if the other conditions + * are true. */ public void updateShowEmptyShadeView() { mShowEmptyShadeView = mBarState != KEYGUARD - && !mView.isQsExpanded() + && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade()) && mView.getVisibleNotificationCount() == 0; + mView.updateEmptyShadeView( mShowEmptyShadeView, mZenModeController.areNotificationsHiddenInShade()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index f97a0675b3a7..dcfa94dec1aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -23,6 +23,8 @@ import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; import static androidx.constraintlayout.widget.ConstraintSet.START; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; +import static com.android.keyguard.KeyguardClockSwitch.LARGE; +import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; @@ -58,6 +60,8 @@ import android.os.SystemClock; import android.os.UserManager; import android.os.VibrationEffect; import android.provider.Settings; +import android.transition.ChangeBounds; +import android.transition.TransitionManager; import android.util.Log; import android.util.MathUtils; import android.view.LayoutInflater; @@ -614,6 +618,8 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardMediaController mKeyguardMediaController; + private boolean mStatusViewCentered = false; + private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { @@ -1021,16 +1027,16 @@ public class NotificationPanelViewController extends PanelViewController { constraintSet.connect( R.id.notification_stack_scroller, START, R.id.qs_edge_guideline, START); - constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END); } else { constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END); constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START); - constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END); } constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth; constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth; constraintSet.applyTo(mNotificationContainerParent); + updateKeyguardStatusViewAlignment(false /* animate */); + mKeyguardMediaController.refreshMediaPosition(); } @@ -1248,7 +1254,14 @@ public class NotificationPanelViewController extends PanelViewController { updateClockAppearance(); } if (!onKeyguard) { - stackScrollerPadding = getUnlockedStackScrollerPadding(); + if (mShouldUseSplitNotificationShade) { + // Quick settings are not on the top of the notifications + // when in split shade mode (they are on the left side), + // so we should not add a padding for them + stackScrollerPadding = 0; + } else { + stackScrollerPadding = getUnlockedStackScrollerPadding(); + } } else { stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; } @@ -1269,7 +1282,12 @@ public class NotificationPanelViewController extends PanelViewController { boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); - mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); + if (hasVisibleNotifications && !mShouldUseSplitNotificationShade) { + mKeyguardStatusViewController.displayClock(SMALL); + } else { + mKeyguardStatusViewController.displayClock(LARGE); + } + updateKeyguardStatusViewAlignment(true /* animate */); int userIconHeight = mKeyguardQsUserSwitchController != null ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0; float expandedFraction = @@ -1313,6 +1331,26 @@ public class NotificationPanelViewController extends PanelViewController { updateClock(); } + private void updateKeyguardStatusViewAlignment(boolean animate) { + boolean hasVisibleNotifications = mNotificationStackScrollLayoutController + .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); + boolean shouldBeCentered = !mShouldUseSplitNotificationShade || !hasVisibleNotifications; + if (mStatusViewCentered != shouldBeCentered) { + mStatusViewCentered = shouldBeCentered; + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(mNotificationContainerParent); + int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline; + constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END); + if (animate) { + ChangeBounds transition = new ChangeBounds(); + transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition); + } + constraintSet.applyTo(mNotificationContainerParent); + } + } + /** * @return the padding of the stackscroller when unlocked */ @@ -2247,7 +2285,8 @@ public class NotificationPanelViewController extends PanelViewController { private void updateQSExpansionEnabledAmbient() { final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight; - mQsExpansionEnabledAmbient = mAmbientState.getScrollY() <= scrollRangeToTop; + mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade + || (mAmbientState.getScrollY() <= scrollRangeToTop); setQsExpansionEnabled(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 022faf78b946..0ff6490aa7c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -469,7 +469,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW if (cb != null) { cb.onStateChanged(mCurrentState.mKeyguardShowing, mCurrentState.mKeyguardOccluded, - mCurrentState.mBouncerShowing); + mCurrentState.mBouncerShowing, + mCurrentState.mDozing); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index cfcea9684c3b..44ed279ba187 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -359,21 +359,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mAnimateChange = state.getAnimateChange(); mAnimationDuration = state.getAnimationDuration(); - mInFrontTint = state.getFrontTint(); - mBehindTint = state.getBehindTint(); - mNotificationsTint = state.getNotifTint(); - mBubbleTint = state.getBubbleTint(); - - mInFrontAlpha = state.getFrontAlpha(); - mBehindAlpha = state.getBehindAlpha(); - mBubbleAlpha = state.getBubbleAlpha(); - mNotificationsAlpha = state.getNotifAlpha(); - if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { - throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: " - + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " - + mNotificationsAlpha); - } - applyStateToAlpha(); + applyState(); // Scrim might acquire focus when user is navigating with a D-pad or a keyboard. // We need to disable focus otherwise AOD would end up with a gray overlay. @@ -637,7 +623,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - private void applyStateToAlpha() { + private void applyState() { + mInFrontTint = mState.getFrontTint(); + mBehindTint = mState.getBehindTint(); + mNotificationsTint = mState.getNotifTint(); + mBubbleTint = mState.getBubbleTint(); + + mInFrontAlpha = mState.getFrontAlpha(); + mBehindAlpha = mState.getBehindAlpha(); + mBubbleAlpha = mState.getBubbleAlpha(); + mNotificationsAlpha = mState.getNotifAlpha(); + + assertAlphasValid(); + if (!mExpansionAffectsAlpha) { return; } @@ -695,6 +693,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mBehindTint = behindTint; } } + + assertAlphasValid(); + } + + private void assertAlphasValid() { if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { throw new IllegalStateException("Scrim opacity is NaN for state: " + mState + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " @@ -734,7 +737,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private void applyAndDispatchState() { - applyStateToAlpha(); + applyState(); if (mUpdatePending) { return; } @@ -1203,6 +1206,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump pw.println(" ScrimController: "); pw.print(" state: "); pw.println(mState); + pw.println(" mClipQsScrim = " + mState.mClipQsScrim); pw.print(" frontScrim:"); pw.print(" viewAlpha="); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 0b6884bcdfa4..5a3acdb71752 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -931,6 +931,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBar.setBouncerShowing(bouncerShowing); } + if (occluded != mLastOccluded || mFirstUpdate) { + mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded); + } if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) { mKeyguardUpdateManager.onKeyguardVisibilityChanged(showing && !occluded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java index f33ff2732cda..ac43b679da0f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java @@ -16,5 +16,6 @@ package com.android.systemui.statusbar.phone; public interface StatusBarWindowCallback { - void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing); + void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing, + boolean isDozing); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 9a25a7078859..3d3b58ae0c1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; @@ -34,6 +36,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.Gravity; import android.view.IWindowManager; +import android.view.Surface; import android.view.ViewGroup; import android.view.WindowManager; @@ -118,21 +121,7 @@ public class StatusBarWindowController { // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. - mLp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - mBarHeight, - WindowManager.LayoutParams.TYPE_STATUS_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - PixelFormat.TRANSLUCENT); - mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; - mLp.token = new Binder(); - mLp.gravity = Gravity.TOP; - mLp.setFitInsetsTypes(0 /* types */); - mLp.setTitle("StatusBar"); - mLp.packageName = mContext.getPackageName(); - mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mLp = getBarLayoutParams(mContext.getDisplay().getRotation()); mWindowManager.addView(mStatusBarView, mLp); mLpChanged.copyFrom(mLp); @@ -141,6 +130,63 @@ public class StatusBarWindowController { calculateStatusBarLocationsForAllRotations(); } + private WindowManager.LayoutParams getBarLayoutParams(int rotation) { + WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); + lp.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot); + } + return lp; + } + + private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { + int height = mBarHeight; + if (INSETS_LAYOUT_GENERALIZATION) { + Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + int defaultAndUpsideDownHeight; + int theOtherHeight; + if (displayBounds.width() > displayBounds.height()) { + defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_landscape); + theOtherHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_portrait); + } else { + defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_portrait); + theOtherHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_landscape); + } + switch (rotation) { + case ROTATION_UNDEFINED: + case Surface.ROTATION_0: + case Surface.ROTATION_180: + height = defaultAndUpsideDownHeight; + break; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + height = theOtherHeight; + break; + } + } + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + height, + WindowManager.LayoutParams.TYPE_STATUS_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + lp.token = new Binder(); + lp.gravity = Gravity.TOP; + lp.setFitInsetsTypes(0 /* types */); + lp.setTitle("StatusBar"); + lp.packageName = mContext.getPackageName(); + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + return lp; + + } + private void calculateStatusBarLocationsForAllRotations() { Rect[] bounds = new Rect[4]; bounds[0] = mContentInsetsProvider diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 5de7846a820e..94467329b6c9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -104,6 +104,7 @@ import android.widget.Toast; import androidx.annotation.Nullable; import com.android.internal.graphics.drawable.BackgroundBlurDrawable; +import com.android.internal.view.RotationPolicy; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Prefs; @@ -400,7 +401,9 @@ public class VolumeDialogImpl implements VolumeDialog, mDialog.setCanceledOnTouchOutside(true); mDialog.setOnShowListener(dialog -> { mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); - if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f); + if (!shouldSlideInVolumeTray()) { + mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f); + } mDialogView.setAlpha(0); mDialogView.animate() .alpha(1) @@ -587,6 +590,10 @@ public class VolumeDialogImpl implements VolumeDialog, return (int) (alpha * 255); } + private boolean shouldSlideInVolumeTray() { + return mContext.getDisplay().getRotation() != RotationPolicy.NATURAL_ROTATION; + } + private boolean isLandscape() { return mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; @@ -1320,7 +1327,7 @@ public class VolumeDialogImpl implements VolumeDialog, hideRingerDrawer(); }, 50)); - if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f); + if (!shouldSlideInVolumeTray()) animator.translationX(mDialogView.getWidth() / 2.0f); animator.start(); checkODICaptionsTooltip(true); mController.notifyVisible(false); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 2216a915b0d9..3be1d3cab869 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.tv.TvPipController; import com.android.wm.shell.pip.tv.TvPipMenuController; @@ -144,10 +145,17 @@ public abstract class TvPipModule { @WMSingleton @Provides + static PipTransitionState providePipTransitionState() { + return new PipTransitionState(); + } + + @WMSingleton + @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, TvPipMenuController tvPipMenuController, SyncTransactionQueue syncTransactionQueue, PipBoundsState pipBoundsState, + PipTransitionState pipTransitionState, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, PipTransitionController pipTransitionController, @@ -157,7 +165,7 @@ public abstract class TvPipModule { PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, - syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm, + syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm, tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index bc956dc85702..f2db4f1cd901 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -64,6 +64,7 @@ import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.onehanded.OneHandedUiEventLogger; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.protolog.ShellProtoLogImpl; +import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -104,7 +105,8 @@ public final class WMShell extends SystemUI // Shell interfaces private final Optional<Pip> mPipOptional; - private final Optional<LegacySplitScreen> mSplitScreenOptional; + private final Optional<LegacySplitScreen> mLegacySplitScreenOptional; + private final Optional<SplitScreen> mSplitScreenOptional; private final Optional<OneHanded> mOneHandedOptional; private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional; private final Optional<ShellCommandHandler> mShellCommandHandler; @@ -120,6 +122,7 @@ public final class WMShell extends SystemUI private final Executor mSysUiMainExecutor; private boolean mIsSysUiStateValid; + private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; @@ -128,7 +131,8 @@ public final class WMShell extends SystemUI @Inject public WMShell(Context context, Optional<Pip> pipOptional, - Optional<LegacySplitScreen> splitScreenOptional, + Optional<LegacySplitScreen> legacySplitScreenOptional, + Optional<SplitScreen> splitScreenOptional, Optional<OneHanded> oneHandedOptional, Optional<HideDisplayCutout> hideDisplayCutoutOptional, Optional<ShellCommandHandler> shellCommandHandler, @@ -149,6 +153,7 @@ public final class WMShell extends SystemUI mScreenLifecycle = screenLifecycle; mSysUiState = sysUiState; mPipOptional = pipOptional; + mLegacySplitScreenOptional = legacySplitScreenOptional; mSplitScreenOptional = splitScreenOptional; mOneHandedOptional = oneHandedOptional; mHideDisplayCutoutOptional = hideDisplayCutoutOptional; @@ -165,6 +170,7 @@ public final class WMShell extends SystemUI mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); + mLegacySplitScreenOptional.ifPresent(this::initLegacySplitScreen); mSplitScreenOptional.ifPresent(this::initSplitScreen); mOneHandedOptional.ifPresent(this::initOneHanded); mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout); @@ -218,8 +224,8 @@ public final class WMShell extends SystemUI } @VisibleForTesting - void initSplitScreen(LegacySplitScreen legacySplitScreen) { - mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() { + void initLegacySplitScreen(LegacySplitScreen legacySplitScreen) { + mLegacySplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override public void onKeyguardVisibilityChanged(boolean showing) { // Hide the divider when keyguard is showing. Even though keyguard/statusbar is @@ -229,6 +235,17 @@ public final class WMShell extends SystemUI legacySplitScreen.onKeyguardVisibilityChanged(showing); } }; + mKeyguardUpdateMonitor.registerCallback(mLegacySplitScreenKeyguardCallback); + } + + @VisibleForTesting + void initSplitScreen(SplitScreen splitScreen) { + mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onKeyguardOccludedChanged(boolean occluded) { + splitScreen.onKeyguardOccludedChanged(occluded); + } + }; mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 6ef74505a681..7e733a9721ef 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -194,11 +194,12 @@ public abstract class WMShellBaseModule { ShellTaskOrganizer organizer, DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler) { + @ShellMainThread Handler mainHandler, + SyncTransactionQueue syncQueue) { return Optional.of(BubbleController.create(context, null /* synchronizer */, floatingContentCoordinator, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, taskStackListener, - uiEventLogger, organizer, displayController, mainExecutor, mainHandler)); + uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue)); } // @@ -424,8 +425,9 @@ public abstract class WMShellBaseModule { @Provides static TaskViewFactoryController provideTaskViewFactoryController( ShellTaskOrganizer shellTaskOrganizer, - @ShellMainThread ShellExecutor mainExecutor) { - return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor); + @ShellMainThread ShellExecutor mainExecutor, + SyncTransactionQueue syncQueue) { + return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue); } // diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 36fd9bee80ab..be7813e19b11 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -48,6 +48,7 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransition; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipAppOpsListener; @@ -184,8 +185,15 @@ public class WMShellModule { @WMSingleton @Provides + static PipTransitionState providePipTransitionState() { + return new PipTransitionState(); + } + + @WMSingleton + @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, SyncTransactionQueue syncTransactionQueue, + PipTransitionState pipTransitionState, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, PhonePipMenuController menuPhoneController, @@ -197,7 +205,7 @@ public class WMShellModule { PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, - syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm, + syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm, menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper, pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); @@ -215,8 +223,9 @@ public class WMShellModule { static PipTransitionController providePipTransitionController(Context context, Transitions transitions, ShellTaskOrganizer shellTaskOrganizer, PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) { - return new PipTransition(context, pipBoundsState, pipMenuController, + PipBoundsState pipBoundsState, PipTransitionState pipTransitionState, + PhonePipMenuController pipMenuController) { + return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController, pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 10ed1d75a533..ce02b8339422 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -19,6 +19,9 @@ package com.android.keyguard; import static android.view.View.GONE; import static android.view.View.VISIBLE; +import static com.android.keyguard.KeyguardClockSwitch.LARGE; +import static com.android.keyguard.KeyguardClockSwitch.SMALL; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; @@ -247,4 +250,36 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { verify(plugin).setStyle(style); } + + @Test + public void switchingToBigClock_makesSmallClockDisappear() { + mKeyguardClockSwitch.switchToClock(LARGE); + + mKeyguardClockSwitch.mClockInAnim.end(); + mKeyguardClockSwitch.mClockOutAnim.end(); + + assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); + assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); + assertThat(mClockFrame.getAlpha()).isEqualTo(0); + } + + @Test + public void switchingToSmallClock_makesBigClockDisappear() { + mKeyguardClockSwitch.switchToClock(SMALL); + + mKeyguardClockSwitch.mClockInAnim.end(); + mKeyguardClockSwitch.mClockOutAnim.end(); + + assertThat(mClockFrame.getAlpha()).isEqualTo(1); + assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE); + // only big clock is removed at switch + assertThat(mLargeClockFrame.getParent()).isNull(); + assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); + } + + @Test + public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { + assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue(); + assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse(); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index ca857c5dd05b..64bdc2e9dc5d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -19,11 +19,13 @@ package com.android.keyguard; import static android.view.WindowInsets.Type.ime; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -33,6 +35,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.MotionEvent; import android.view.WindowInsetsController; import androidx.test.filters.SmallTest; @@ -43,6 +46,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -58,6 +62,7 @@ import org.mockito.junit.MockitoRule; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { + private static final int VIEW_WIDTH = 1600; @Rule public MockitoRule mRule = MockitoJUnit.rule(); @@ -100,6 +105,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private EmergencyButtonController mEmergencyButtonController; @Mock private Resources mResources; + @Mock + private FalsingCollector mFalsingCollector; private Configuration mConfiguration; private KeyguardSecurityContainerController mKeyguardSecurityContainerController; @@ -112,7 +119,9 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED. when(mResources.getConfiguration()).thenReturn(mConfiguration); + when(mView.getContext()).thenReturn(mContext); when(mView.getResources()).thenReturn(mResources); + when(mView.getWidth()).thenReturn(VIEW_WIDTH); when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class))) .thenReturn(mAdminSecondaryLockScreenController); when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); @@ -131,7 +140,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, mKeyguardSecurityViewFlipperController, - mConfigurationController) + mConfigurationController, mFalsingCollector) .create(mSecurityCallback); } @@ -169,18 +178,156 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { public void onResourcesUpdate_callsThroughOnRotationChange() { // Rotation is the same, shouldn't cause an update mKeyguardSecurityContainerController.updateResources(); - verify(mView, times(0)).updateLayoutForSecurityMode(any()); + verify(mView, times(0)).setOneHandedMode(anyBoolean()); // Update rotation. Should trigger update mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; mKeyguardSecurityContainerController.updateResources(); - verify(mView, times(1)).updateLayoutForSecurityMode(any()); + verify(mView, times(1)).setOneHandedMode(anyBoolean()); } @Test - public void updateKeyguardPosition_callsThroughToView() { + public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() { + when(mView.isOneHandedMode()).thenReturn(true); + mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f); + verify(mView).setOneHandedModeLeftAligned(true, false); + + mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2); + verify(mView).setOneHandedModeLeftAligned(false, false); + } + + @Test + public void updateKeyguardPosition_ignoredInTwoHandedMode() { + when(mView.isOneHandedMode()).thenReturn(false); mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f); - verify(mView).updateKeyguardPosition(1.0f); + verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean()); + } + + private void touchDownLeftSide() { + mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent( + MotionEvent.obtain( + /* downTime= */0, + /* eventTime= */0, + MotionEvent.ACTION_DOWN, + /* x= */VIEW_WIDTH / 3f, + /* y= */0, + /* metaState= */0)); + } + + private void touchDownRightSide() { + mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent( + MotionEvent.obtain( + /* downTime= */0, + /* eventTime= */0, + MotionEvent.ACTION_DOWN, + /* x= */(VIEW_WIDTH / 3f) * 2, + /* y= */0, + /* metaState= */0)); + } + + @Test + public void onInterceptTap_inhibitsFalsingInOneHandedMode() { + when(mView.isOneHandedMode()).thenReturn(true); + when(mView.isOneHandedModeLeftAligned()).thenReturn(true); + + touchDownLeftSide(); + verify(mFalsingCollector, never()).avoidGesture(); + + // Now on the right. + touchDownRightSide(); + verify(mFalsingCollector).avoidGesture(); + + // Move and re-test + reset(mFalsingCollector); + when(mView.isOneHandedModeLeftAligned()).thenReturn(false); + + // On the right... + touchDownRightSide(); + verify(mFalsingCollector, never()).avoidGesture(); + + touchDownLeftSide(); + verify(mFalsingCollector).avoidGesture(); + } + + @Test + public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() { + setUpKeyguardFlags( + /* deviceConfigCanUseOneHandedKeyguard= */false, + /* sysuiResourceCanUseOneHandedKeyguard= */false); + + when(mKeyguardSecurityViewFlipperController.getSecurityView( + eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) + .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); + + mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); + verify(mView).setOneHandedMode(false); + } + + @Test + public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() { + setUpKeyguardFlags( + /* deviceConfigCanUseOneHandedKeyguard= */false, + /* sysuiResourceCanUseOneHandedKeyguard= */true); + + when(mKeyguardSecurityViewFlipperController.getSecurityView( + eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) + .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); + + mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); + verify(mView).setOneHandedMode(false); + } + + @Test + public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() { + setUpKeyguardFlags( + /* deviceConfigCanUseOneHandedKeyguard= */true, + /* sysuiResourceCanUseOneHandedKeyguard= */false); + + when(mKeyguardSecurityViewFlipperController.getSecurityView( + eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) + .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); + + mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); + verify(mView).setOneHandedMode(false); + } + + @Test + public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() { + setUpKeyguardFlags( + /* deviceConfigCanUseOneHandedKeyguard= */true, + /* sysuiResourceCanUseOneHandedKeyguard= */true); + + when(mKeyguardSecurityViewFlipperController.getSecurityView( + eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class))) + .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); + + mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); + verify(mView).setOneHandedMode(true); + } + + @Test + public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() { + setUpKeyguardFlags( + /* deviceConfigCanUseOneHandedKeyguard= */true, + /* sysuiResourceCanUseOneHandedKeyguard= */true); + + when(mKeyguardSecurityViewFlipperController.getSecurityView( + eq(SecurityMode.Password), any(KeyguardSecurityCallback.class))) + .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); + + mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password); + verify(mView).setOneHandedMode(false); + } + + private void setUpKeyguardFlags( + boolean deviceConfigCanUseOneHandedKeyguard, + boolean sysuiResourceCanUseOneHandedKeyguard) { + when(mResources.getBoolean( + com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) + .thenReturn(deviceConfigCanUseOneHandedKeyguard); + when(mResources.getBoolean( + R.bool.can_use_one_handed_bouncer)) + .thenReturn(sysuiResourceCanUseOneHandedKeyguard); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index f5916e748f04..02763235a8ca 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -28,7 +28,6 @@ import static org.mockito.Mockito.when; import android.graphics.Insets; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.testing.TestableResources; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; @@ -37,8 +36,6 @@ import android.widget.FrameLayout; import androidx.test.filters.SmallTest; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -57,11 +54,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { private static final int FAKE_MEASURE_SPEC = View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY); - private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN; - private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password; - - - @Rule public MockitoRule mRule = MockitoJUnit.rule(); @@ -90,45 +82,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { } @Test - public void onMeasure_usesFullWidthWithoutOneHandedMode() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */false, - /* sysuiResourceCanUseOneHandedKeyguard= */ false, - ONE_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesFullWidthWithDeviceFlagDisabled() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */false, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - ONE_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesFullWidthWithSysUIFlagDisabled() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ false, - ONE_HANDED_SECURITY_MODE); - - mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); - } - - @Test - public void onMeasure_usesHalfWidthWithFlagsEnabled() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - ONE_HANDED_SECURITY_MODE); + public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() { + mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */true); int halfWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY); @@ -138,11 +93,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { } @Test - public void onMeasure_usesFullWidthForFullScreenIme() { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - TWO_HANDED_SECURITY_MODE); + public void onMeasure_usesFullWidthWithOneHandedModeDisabled() { + mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false); mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); @@ -153,10 +105,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { int imeInsetAmount = 100; int systemBarInsetAmount = 10; - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */false, - /* sysuiResourceCanUseOneHandedKeyguard= */ false, - ONE_HANDED_SECURITY_MODE); + mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false); Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount); Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount); @@ -180,10 +129,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { int imeInsetAmount = 0; int systemBarInsetAmount = 10; - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */false, - /* sysuiResourceCanUseOneHandedKeyguard= */ false, - ONE_HANDED_SECURITY_MODE); + mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false); Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount); Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount); @@ -201,56 +147,41 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec); } - private void setupForUpdateKeyguardPosition(SecurityMode securityMode) { - setUpKeyguard( - /* deviceConfigCanUseOneHandedKeyguard= */true, - /* sysuiResourceCanUseOneHandedKeyguard= */ true, - securityMode); + private void setupForUpdateKeyguardPosition(boolean oneHandedMode) { + mKeyguardSecurityContainer.setOneHandedMode(oneHandedMode); + mKeyguardSecurityContainer.setOneHandedModeLeftAligned(true, false); mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC); mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH); - // Start off left-aligned. This should happen anyway, but just do this to ensure - // definitely move to the left. - mKeyguardSecurityContainer.updateKeyguardPosition(0.0f); - // Clear any interactions with the mock so we know the interactions definitely come from the // below testing. reset(mSecurityViewFlipper); } @Test - public void updateKeyguardPosition_movesKeyguard() { - setupForUpdateKeyguardPosition(ONE_HANDED_SECURITY_MODE); + public void setIsLeftAligned_movesKeyguard() { + setupForUpdateKeyguardPosition(/* oneHandedMode= */ true); - mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f); + mKeyguardSecurityContainer.setOneHandedModeLeftAligned( + /* leftAligned= */false, /* animate= */false); verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f); - mKeyguardSecurityContainer.updateKeyguardPosition(0.0f); + mKeyguardSecurityContainer.setOneHandedModeLeftAligned( + /* leftAligned= */true, /* animate= */false); verify(mSecurityViewFlipper).setTranslationX(0.0f); } @Test - public void updateKeyguardPosition_doesntMoveTwoHandedKeyguard() { - setupForUpdateKeyguardPosition(TWO_HANDED_SECURITY_MODE); + public void setIsLeftAligned_doesntMoveTwoHandedKeyguard() { + setupForUpdateKeyguardPosition(/* oneHandedMode= */ false); - mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f); + mKeyguardSecurityContainer.setOneHandedModeLeftAligned( + /* leftAligned= */false, /* animate= */false); verify(mSecurityViewFlipper, never()).setTranslationX(anyInt()); - mKeyguardSecurityContainer.updateKeyguardPosition(0.0f); + mKeyguardSecurityContainer.setOneHandedModeLeftAligned( + /* leftAligned= */true, /* animate= */false); verify(mSecurityViewFlipper, never()).setTranslationX(anyInt()); } - - private void setUpKeyguard( - boolean deviceConfigCanUseOneHandedKeyguard, - boolean sysuiResourceCanUseOneHandedKeyguard, - SecurityMode securityMode) { - TestableResources testableResources = mContext.getOrCreateTestableResources(); - testableResources.addOverride( - com.android.internal.R.bool.config_enableDynamicKeyguardPositioning, - deviceConfigCanUseOneHandedKeyguard); - testableResources.addOverride(R.bool.can_use_one_handed_bouncer, - sysuiResourceCanUseOneHandedKeyguard); - mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java index 9621bedd9210..8bb9d423fa92 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java @@ -16,9 +16,6 @@ package com.android.systemui.accessibility; -import static android.view.WindowInsets.Type.systemGestures; - -import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.view.Display; @@ -79,14 +76,11 @@ public class TestableWindowManager implements WindowManager { @Override public WindowMetrics getCurrentWindowMetrics() { - final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10); - final WindowInsets insets = new WindowInsets.Builder() - .setInsets(systemGestures(), systemGesturesInsets) - .build(); + final WindowMetrics realMetrics = mWindowManager.getCurrentWindowMetrics(); final WindowMetrics windowMetrics = new WindowMetrics( - mWindowBounds == null ? mWindowManager.getCurrentWindowMetrics().getBounds() + mWindowBounds == null ? realMetrics.getBounds() : mWindowBounds, - mWindowInsets == null ? insets : mWindowInsets); + mWindowInsets == null ? realMetrics.getWindowInsets() : mWindowInsets); return windowMetrics; } @@ -106,10 +100,20 @@ public class TestableWindowManager implements WindowManager { return (WindowManager.LayoutParams) mView.getLayoutParams(); } + /** + * Sets the given window bounds to current window metrics. + * + * @param bounds the window bounds + */ public void setWindowBounds(Rect bounds) { mWindowBounds = bounds; } + /** + * Sets the given window insets to the current window metics. + * + * @param insets the window insets. + */ public void setWindowInsets(WindowInsets insets) { mWindowInsets = insets; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index f62069d35d35..76cc7a0eda13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.accessibility; import static android.view.Choreographer.FrameCallback; +import static android.view.WindowInsets.Type.systemGestures; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP; @@ -45,6 +46,8 @@ import android.app.Instrumentation; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; +import android.graphics.Insets; +import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; import android.os.SystemClock; @@ -55,6 +58,7 @@ import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.view.View; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -239,11 +243,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY); mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE); + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); }); } @Test - public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() { + public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() { final Display display = Mockito.spy(mContext.getDisplay()); when(display.getRotation()).thenReturn(Surface.ROTATION_90); when(mContext.getDisplay()).thenReturn(display); @@ -251,13 +257,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); }); + final PointF expectedCenter = new PointF(mWindowMagnificationController.getCenterY(), + mWindowMagnificationController.getCenterX()); + final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds()); + // Rotate the window 90 degrees. + windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom, + windowBounds.right); + mWindowManager.setWindowBounds(windowBounds); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION); }); assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation); - // The first invocation is called when the surface is created. + final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(), + mWindowMagnificationController.getCenterY()); + assertEquals(expectedCenter, actualCenter); verify(mWindowManager, times(2)).updateViewLayout(any(), any()); } @@ -275,6 +290,33 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } @Test + public void onScreenSizeChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() { + // The default position is at the center of the screen. + final float expectedRatio = 0.5f; + final Rect testWindowBounds = new Rect( + mWindowManager.getCurrentWindowMetrics().getBounds()); + testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, + testWindowBounds.right + 100, testWindowBounds.bottom + 100); + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); + }); + mWindowManager.setWindowBounds(testWindowBounds); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + + // The ratio of center to window size should be the same. + assertEquals(expectedRatio, + mWindowMagnificationController.getCenterX() / testWindowBounds.width(), + 0); + assertEquals(expectedRatio, + mWindowMagnificationController.getCenterY() / testWindowBounds.height(), + 0); + } + + @Test public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() { mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, @@ -419,9 +461,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } @Test - public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() { - final WindowManager wm = mContext.getSystemService(WindowManager.class); - final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() { + final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + final WindowInsets testInsets = new WindowInsets.Builder() + .setInsets(systemGestures(), Insets.of(0, 0, 0, 10)) + .build(); + mWindowManager.setWindowInsets(testInsets); mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index 578c2d976cce..32abbc755c56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -104,7 +104,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private IWindowManager mWindowManager; @Mock private Executor mBackgroundExecutor; @Mock private UiEventLogger mUiEventLogger; - @Mock private GlobalActionsInfoProvider mInfoProvider; @Mock private RingerModeTracker mRingerModeTracker; @Mock private RingerModeLiveData mRingerModeLiveData; @Mock private SysUiState mSysUiState; @@ -151,7 +150,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mWindowManager, mBackgroundExecutor, mUiEventLogger, - mInfoProvider, mRingerModeTracker, mSysUiState, mHandler, diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt deleted file mode 100644 index 302a8d3f2efa..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2021 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.google.android.systemui.globalactions - -import android.content.Context -import android.content.SharedPreferences -import android.content.res.Configuration -import android.content.res.Resources -import android.service.quickaccesswallet.QuickAccessWalletClient -import android.testing.AndroidTestingRunner -import android.view.ViewGroup -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.controls.controller.ControlsController -import com.android.systemui.globalactions.GlobalActionsInfoProvider -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.SysuiTestCase -import junit.framework.Assert.assertFalse -import junit.framework.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyObject -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.spy -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever - -private const val PREFERENCE = "global_actions_info_prefs" -private const val KEY_VIEW_COUNT = "view_count" - -private fun <T> eq(value: T): T = Mockito.eq(value) ?: value - -@SmallTest -@RunWith(AndroidTestingRunner::class) -class GlobalActionsInfoProviderTest : SysuiTestCase() { - - @Mock private lateinit var walletClient: QuickAccessWalletClient - @Mock private lateinit var controlsController: ControlsController - @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var mockContext: Context - @Mock private lateinit var mockResources: Resources - @Mock private lateinit var sharedPrefs: SharedPreferences - @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor - - private lateinit var infoProvider: GlobalActionsInfoProvider - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - mockContext = spy(context) - mockResources = spy(context.resources) - whenever(mockContext.resources).thenReturn(mockResources) - whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info)) - .thenReturn(true) - whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt())) - .thenReturn(sharedPrefs) - whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor) - whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor) - whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean())) - .thenReturn(sharedPrefsEditor) - - infoProvider = GlobalActionsInfoProvider( - mockContext, - walletClient, - controlsController, - activityStarter - ) - } - - @Test - fun testIsEligible_noCards() { - whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false) - whenever(walletClient.isWalletFeatureAvailable).thenReturn(false) - - assertFalse(infoProvider.shouldShowMessage()) - } - - @Test - fun testIsEligible_hasCards() { - whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false) - whenever(walletClient.isWalletFeatureAvailable).thenReturn(true) - - assertTrue(infoProvider.shouldShowMessage()) - } - - @Test - fun testNotEligible_shouldNotShow() { - whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info)) - .thenReturn(false) - - assertFalse(infoProvider.shouldShowMessage()) - } - - @Test - fun testTooManyButtons_doesNotAdd() { - val configuration = Configuration() - configuration.orientation = Configuration.ORIENTATION_LANDSCAPE - whenever(mockResources.configuration).thenReturn(configuration) - - val parent = mock(ViewGroup::class.java) - infoProvider.addPanel(mockContext, parent, 5, { }) - - verify(parent, never()).addView(anyObject(), anyInt()) - } - - @Test - fun testLimitTimesShown() { - whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4) - - assertFalse(infoProvider.shouldShowMessage()) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java index d2527c679a13..e9a7c73558c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -81,6 +81,8 @@ public class NavigationBarControllerTest extends SysuiTestCase { private NavigationBar mDefaultNavBar; private NavigationBar mSecondaryNavBar; + private CommandQueue mCommandQueue = mock(CommandQueue.class); + private static final int SECONDARY_DISPLAY = 1; @Before @@ -99,7 +101,7 @@ public class NavigationBarControllerTest extends SysuiTestCase { mock(StatusBarStateController.class), mock(SysUiState.class), mock(BroadcastDispatcher.class), - mock(CommandQueue.class), + mCommandQueue, Optional.of(mock(Pip.class)), Optional.of(mock(LegacySplitScreen.class)), Optional.of(mock(Recents.class)), @@ -112,6 +114,8 @@ public class NavigationBarControllerTest extends SysuiTestCase { mock(UiEventLogger.class), mock(NavigationBarOverlayController.class), mock(ConfigurationController.class), + mock(NavigationBarA11yHelper.class), + mock(TaskbarDelegate.class), mock(UserTracker.class))); initializeNavigationBars(); } @@ -275,4 +279,9 @@ public class NavigationBarControllerTest extends SysuiTestCase { verify(mSecondaryNavBar).disableAnimationsDuringHide(eq(500L)); } + + @Test + public void test3ButtonTaskbarFlagDisabledNoRegister() { + verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java index eac68f6e76f4..b9919767d63f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java @@ -31,9 +31,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestableContext; -import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; -import com.android.systemui.navigationbar.RotationButton; -import com.android.systemui.navigationbar.RotationButtonController; import com.android.systemui.statusbar.policy.RotationLockController; import org.junit.Before; @@ -46,7 +43,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class NavigationBarRotationContextTest extends SysuiTestCase { - static final int RES_UNDEF = 0; static final int DEFAULT_ROTATE = 0; @Rule @@ -66,7 +62,6 @@ public class NavigationBarRotationContextTest extends SysuiTestCase { mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {}); // Due to a mockito issue, only spy the object after setting the initial state mRotationButtonController = spy(mRotationButtonController); - final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class); doReturn(view).when(mRotationButton).getCurrentView(); doReturn(true).when(mRotationButton).acceptRotationProposal(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index a570675b442e..c606a430e325 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -280,6 +280,7 @@ public class NavigationBarTest extends SysuiTestCase { mHandler, mock(NavigationBarOverlayController.class), mUiEventLogger, + mock(NavigationBarA11yHelper.class), mock(UserTracker.class))); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index f376e88b2cb1..6ee2f2026deb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -92,7 +92,7 @@ import org.mockito.MockitoAnnotations; */ @SmallTest @RunWith(AndroidTestingRunner.class) -public class NotificationStackScrollerControllerTest extends SysuiTestCase { +public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private HeadsUpManagerPhone mHeadsUpManager; @@ -232,16 +232,15 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( - true /* visible */, - - true /* notifVisibleInShade */); + /* visible= */ true, + /* notifVisibleInShade= */ true); setupShowEmptyShadeViewState(stateListener, false); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( - false /* visible */, - true /* notifVisibleInShade */); + /* visible= */ false, + /* notifVisibleInShade= */ true); } @Test @@ -257,15 +256,42 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( - true /* visible */, - false /* notifVisibleInShade */); + /* visible= */ true, + /* notifVisibleInShade= */ false); setupShowEmptyShadeViewState(stateListener, false); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( - false /* visible */, - false /* notifVisibleInShade */); + /* visible= */ false, + /* notifVisibleInShade= */ false); + } + + @Test + public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + verify(mSysuiStatusBarStateController).addCallback( + mStateListenerArgumentCaptor.capture(), anyInt()); + StatusBarStateController.StateListener stateListener = + mStateListenerArgumentCaptor.getValue(); + when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true); + stateListener.onStateChanged(SHADE); + mController.getView().removeAllViews(); + + mController.setQsExpanded(false); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + /* visible= */ true, + /* notifVisibleInShade= */ false); + + mController.setQsExpanded(true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + /* visible= */ true, + /* notifVisibleInShade= */ false); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index a6fc02d7dafc..1540b14e172e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.keyguard.KeyguardClockSwitch.LARGE; +import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; @@ -35,6 +37,7 @@ import static org.mockito.Mockito.clearInvocations; 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.verify; import static org.mockito.Mockito.when; @@ -573,17 +576,19 @@ public class NotificationPanelViewTest extends SysuiTestCase { } @Test - public void testKeyguardStatusView_isAlignedToGuidelineInSplitShadeMode() { - mNotificationPanelViewController.updateResources(); + public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() { + mStatusBarStateController.setState(KEYGUARD); + enableSplitShade(); + when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2); + mNotificationPanelViewController.updateResources(); assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd) - .isEqualTo(ConstraintSet.PARENT_ID); + .isEqualTo(R.id.qs_edge_guideline); - enableSplitShade(); + when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); mNotificationPanelViewController.updateResources(); - assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd) - .isEqualTo(R.id.qs_edge_guideline); + .isEqualTo(ConstraintSet.PARENT_ID); } @Test @@ -744,6 +749,38 @@ public class NotificationPanelViewTest extends SysuiTestCase { verify(mTapAgainViewController).show(); } + @Test + public void testSwitchesToCorrectClockInSinglePaneShade() { + mStatusBarStateController.setState(KEYGUARD); + + when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); + triggerPositionClockAndNotifications(); + verify(mKeyguardStatusViewController).displayClock(LARGE); + + when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); + mNotificationPanelViewController.closeQs(); + verify(mKeyguardStatusViewController).displayClock(SMALL); + } + + @Test + public void testSwitchesToCorrectClockInSplitShade() { + mStatusBarStateController.setState(KEYGUARD); + enableSplitShade(); + + when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); + triggerPositionClockAndNotifications(); + verify(mKeyguardStatusViewController).displayClock(LARGE); + + when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); + triggerPositionClockAndNotifications(); + verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE); + verify(mKeyguardStatusViewController, never()).displayClock(SMALL); + } + + private void triggerPositionClockAndNotifications() { + mNotificationPanelViewController.closeQs(); + } + private FalsingManager.FalsingTapListener getFalsingTapListener() { for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { listener.onViewAttachedToWindow(mView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 678b193073c2..2685b76d2650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -576,6 +576,49 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.BOUNCER); + + mScrimController.setClipsQsScrim(false); + + finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be visible without tint + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, OPAQUE)); + assertScrimTinted(Map.of( + mScrimInFront, false, + mScrimBehind, false, + mNotificationsScrim, false + )); + } + + @Test + public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() { + mScrimController.setClipsQsScrim(false); + mScrimController.transitionTo(ScrimState.BOUNCER); + + mScrimController.setClipsQsScrim(true); + + finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be clipping QS + // Notif scrim should be visible without tint + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, OPAQUE, + mScrimBehind, OPAQUE)); + assertScrimTinted(Map.of( + mScrimInFront, false, + mScrimBehind, true, + mNotificationsScrim, false + )); + } + + @Test public void transitionToBouncer() { mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED); finishAnimationsImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index f243077afc93..d6492c610a4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -120,6 +120,7 @@ import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; import com.google.common.collect.ImmutableList; @@ -331,7 +332,8 @@ public class BubblesTest extends SysuiTestCase { mPositioner, mock(DisplayController.class), syncExecutor, - mock(Handler.class)); + mock(Handler.class), + mock(SyncTransactionQueue.class)); mBubbleController.setExpandListener(mBubbleExpandListener); spyOn(mBubbleController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index e4c78009f491..db8a08cdc5e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -100,6 +100,7 @@ import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.Before; @@ -275,7 +276,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mPositioner, mock(DisplayController.class), syncExecutor, - mock(Handler.class)); + mock(Handler.class), + mock(SyncTransactionQueue.class)); mBubbleController.setExpandListener(mBubbleExpandListener); spyOn(mBubbleController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java index cd5aa9a3f9dc..7b77cb0b5b30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -32,6 +32,7 @@ import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; /** @@ -54,11 +55,12 @@ public class TestableBubbleController extends BubbleController { BubblePositioner positioner, DisplayController displayController, ShellExecutor shellMainExecutor, - Handler shellMainHandler) { + Handler shellMainHandler, + SyncTransactionQueue syncQueue) { super(context, data, Runnable::run, floatingContentCoordinator, dataRepository, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController, - shellMainExecutor, shellMainHandler); + shellMainExecutor, shellMainHandler, syncQueue); setInflateSynchronously(true); initialize(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 5691660b4882..8480702c57c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -41,6 +41,7 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; import org.junit.Test; @@ -69,6 +70,7 @@ public class WMShellTest extends SysuiTestCase { @Mock SysUiState mSysUiState; @Mock Pip mPip; @Mock LegacySplitScreen mLegacySplitScreen; + @Mock SplitScreen mSplitScreen; @Mock OneHanded mOneHanded; @Mock HideDisplayCutout mHideDisplayCutout; @Mock WakefulnessLifecycle mWakefulnessLifecycle; @@ -81,7 +83,7 @@ public class WMShellTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen), - Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), + Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle, @@ -96,8 +98,15 @@ public class WMShellTest extends SysuiTestCase { } @Test + public void initLegacySplitScreen_registersCallbacks() { + mWMShell.initLegacySplitScreen(mLegacySplitScreen); + + verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); + } + + @Test public void initSplitScreen_registersCallbacks() { - mWMShell.initSplitScreen(mLegacySplitScreen); + mWMShell.initSplitScreen(mSplitScreen); verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index e9c9899023b1..ee80daeff87d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -21,8 +21,13 @@ import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILIT import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS; import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP; import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK; +import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; @@ -31,6 +36,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; @@ -103,10 +109,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ FingerprintGestureDispatcher.FingerprintGestureClient { private static final boolean DEBUG = false; private static final String LOG_TAG = "AbstractAccessibilityServiceConnection"; - private static final String TRACE_A11Y_SERVICE_CONNECTION = - LOG_TAG + ".IAccessibilityServiceConnection"; - private static final String TRACE_A11Y_SERVICE_CLIENT = - LOG_TAG + ".IAccessibilityServiceClient"; + private static final String TRACE_SVC_CONN = LOG_TAG + ".IAccessibilityServiceConnection"; + private static final String TRACE_SVC_CLIENT = LOG_TAG + ".IAccessibilityServiceClient"; + private static final String TRACE_WM = "WindowManagerInternal"; private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000; protected static final String TAKE_SCREENSHOT = "takeScreenshot"; @@ -298,9 +303,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return false; } try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onKeyEvent", - keyEvent + ", " + sequenceNumber); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber); } mServiceInterface.onKeyEvent(keyEvent, sequenceNumber); } catch (RemoteException e) { @@ -365,17 +369,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setOnKeyEventResult(boolean handled, int sequence) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setOnKeyEventResult", - "handled=" + handled + ";sequence=" + sequence); + if (svcConnTracingEnabled()) { + logTraceSvcConn("setOnKeyEventResult", "handled=" + handled + ";sequence=" + sequence); } mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence); } @Override public AccessibilityServiceInfo getServiceInfo() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getServiceInfo"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getServiceInfo", ""); } synchronized (mLock) { return mAccessibilityServiceInfo; @@ -393,8 +396,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setServiceInfo(AccessibilityServiceInfo info) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setServiceInfo", "info=" + info); + if (svcConnTracingEnabled()) { + logTraceSvcConn("setServiceInfo", "info=" + info); } final long identity = Binder.clearCallingIdentity(); try { @@ -421,8 +424,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Nullable @Override public AccessibilityWindowInfo.WindowListSparseArray getWindows() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindows"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getWindows", ""); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -458,8 +461,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public AccessibilityWindowInfo getWindow(int windowId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindow", "windowId=" + windowId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getWindow", "windowId=" + windowId); } synchronized (mLock) { int displayId = Display.INVALID_DISPLAY; @@ -496,8 +499,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ long accessibilityNodeId, String viewIdResName, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByViewId", + if (svcConnTracingEnabled()) { + logTraceSvcConn("findAccessibilityNodeInfosByViewId", "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId=" + accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId=" + interactionId + ";callback=" + callback + ";interrogatingTid=" @@ -539,6 +542,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); final long identityToken = Binder.clearCallingIdentity(); + if (intConnTracingEnabled()) { + logTraceIntConn("findAccessibilityNodeInfosByViewId", + accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";" + + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + + ";" + interrogatingTid + ";" + spec); + } try { connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags, @@ -564,8 +573,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ long accessibilityNodeId, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByText", + if (svcConnTracingEnabled()) { + logTraceSvcConn("findAccessibilityNodeInfosByText", "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId + ";callback=" + callback + ";interrogatingTid=" + interrogatingTid); @@ -606,6 +615,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); final long identityToken = Binder.clearCallingIdentity(); + if (intConnTracingEnabled()) { + logTraceIntConn("findAccessibilityNodeInfosByText", + accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";" + + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + + ";" + interrogatingTid + ";" + spec); + } try { connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId, text, partialInteractiveRegion, interactionId, callback, mFetchFlags, @@ -631,13 +646,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int accessibilityWindowId, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, long interrogatingTid, Bundle arguments) throws RemoteException { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace( - TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfoByAccessibilityId", + if (svcConnTracingEnabled()) { + logTraceSvcConn("findAccessibilityNodeInfoByAccessibilityId", "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId=" - + accessibilityNodeId + ";interactionId=" + interactionId + ";callback=" - + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid - + ";arguments=" + arguments); + + accessibilityNodeId + ";interactionId=" + interactionId + ";callback=" + + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid + + ";arguments=" + arguments); } final int resolvedWindowId; RemoteAccessibilityConnection connection; @@ -675,6 +689,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); final long identityToken = Binder.clearCallingIdentity(); + if (intConnTracingEnabled()) { + logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId", + accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";" + + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";" + + interrogatingTid + ";" + spec + ";" + arguments); + } try { connection.getRemote().findAccessibilityNodeInfoByAccessibilityId( accessibilityNodeId, partialInteractiveRegion, interactionId, callback, @@ -700,12 +720,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int focusType, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findFocus", + if (svcConnTracingEnabled()) { + logTraceSvcConn("findFocus", "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId=" - + accessibilityNodeId + ";focusType=" + focusType + ";interactionId=" - + interactionId + ";callback=" + callback + ";interrogatingTid=" - + interrogatingTid); + + accessibilityNodeId + ";focusType=" + focusType + ";interactionId=" + + interactionId + ";callback=" + callback + ";interrogatingTid=" + + interrogatingTid); } final int resolvedWindowId; RemoteAccessibilityConnection connection; @@ -743,6 +763,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); final long identityToken = Binder.clearCallingIdentity(); + if (intConnTracingEnabled()) { + logTraceIntConn("findFocus", + accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";" + + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + + ";" + interrogatingTid + ";" + spec); + } try { connection.getRemote().findFocus(accessibilityNodeId, focusType, partialInteractiveRegion, interactionId, callback, mFetchFlags, @@ -768,12 +794,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int direction, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".focusSearch", + if (svcConnTracingEnabled()) { + logTraceSvcConn("focusSearch", "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId=" - + accessibilityNodeId + ";direction=" + direction + ";interactionId=" - + interactionId + ";callback=" + callback + ";interrogatingTid=" - + interrogatingTid); + + accessibilityNodeId + ";direction=" + direction + ";interactionId=" + + interactionId + ";callback=" + callback + ";interrogatingTid=" + + interrogatingTid); } final int resolvedWindowId; RemoteAccessibilityConnection connection; @@ -810,6 +836,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId, interrogatingPid, interrogatingTid); final long identityToken = Binder.clearCallingIdentity(); + if (intConnTracingEnabled()) { + logTraceIntConn("focusSearch", + accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion + + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";" + + interrogatingPid + ";" + interrogatingTid + ";" + spec); + } try { connection.getRemote().focusSearch(accessibilityNodeId, direction, partialInteractiveRegion, interactionId, callback, mFetchFlags, @@ -832,17 +864,17 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void sendGesture(int sequence, ParceledListSlice gestureSteps) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".sendGesture", - "sequence=" + sequence + ";gestureSteps=" + gestureSteps); + if (svcConnTracingEnabled()) { + logTraceSvcConn( + "sendGesture", "sequence=" + sequence + ";gestureSteps=" + gestureSteps); } } @Override public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".dispatchGesture", "sequence=" - + sequence + ";gestureSteps=" + gestureSteps + ";displayId=" + displayId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("dispatchGesture", "sequence=" + sequence + ";gestureSteps=" + + gestureSteps + ";displayId=" + displayId); } } @@ -851,12 +883,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) throws RemoteException { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performAccessibilityAction", + if (svcConnTracingEnabled()) { + logTraceSvcConn("performAccessibilityAction", "accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId=" - + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments - + ";interactionId=" + interactionId + ";callback=" + callback - + ";interrogatingTid=" + interrogatingTid); + + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments + + ";interactionId=" + interactionId + ";callback=" + callback + + ";interrogatingTid=" + interrogatingTid); } final int resolvedWindowId; synchronized (mLock) { @@ -879,9 +911,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public boolean performGlobalAction(int action) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performGlobalAction", - "action=" + action); + if (svcConnTracingEnabled()) { + logTraceSvcConn("performGlobalAction", "action=" + action); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -893,8 +924,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSystemActions"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getSystemActions", ""); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -906,9 +937,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public boolean isFingerprintGestureDetectionAvailable() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace( - TRACE_A11Y_SERVICE_CONNECTION + ".isFingerprintGestureDetectionAvailable"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("isFingerprintGestureDetectionAvailable", ""); } if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { return false; @@ -923,9 +953,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public float getMagnificationScale(int displayId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationScale", - "displayId=" + displayId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getMagnificationScale", "displayId=" + displayId); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -942,9 +971,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public Region getMagnificationRegion(int displayId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationRegion", - "displayId=" + displayId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getMagnificationRegion", "displayId=" + displayId); } synchronized (mLock) { final Region region = Region.obtain(); @@ -970,9 +998,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public float getMagnificationCenterX(int displayId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterX", - "displayId=" + displayId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getMagnificationCenterX", "displayId=" + displayId); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -996,9 +1023,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public float getMagnificationCenterY(int displayId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterY", - "displayId=" + displayId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getMagnificationCenterY", "displayId=" + displayId); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -1032,9 +1058,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public boolean resetMagnification(int displayId, boolean animate) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".resetMagnification", - "displayId=" + displayId + ";animate=" + animate); + if (svcConnTracingEnabled()) { + logTraceSvcConn("resetMagnification", "displayId=" + displayId + ";animate=" + animate); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -1058,10 +1083,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationScaleAndCenter", + if (svcConnTracingEnabled()) { + logTraceSvcConn("setMagnificationScaleAndCenter", "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX - + ";centerY=" + centerY + ";animate=" + animate); + + ";centerY=" + centerY + ";animate=" + animate); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -1087,8 +1112,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setMagnificationCallbackEnabled(int displayId, boolean enabled) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationCallbackEnabled", + if (svcConnTracingEnabled()) { + logTraceSvcConn("setMagnificationCallbackEnabled", "displayId=" + displayId + ";enabled=" + enabled); } mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled); @@ -1100,18 +1125,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setSoftKeyboardCallbackEnabled(boolean enabled) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardCallbackEnabled", - "enabled=" + enabled); + if (svcConnTracingEnabled()) { + logTraceSvcConn("setSoftKeyboardCallbackEnabled", "enabled=" + enabled); } mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled); } @Override public void takeScreenshot(int displayId, RemoteCallback callback) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".takeScreenshot", - "displayId=" + displayId + ";callback=" + callback); + if (svcConnTracingEnabled()) { + logTraceSvcConn("takeScreenshot", "displayId=" + displayId + ";callback=" + callback); } final long currentTimestamp = SystemClock.uptimeMillis(); if (mRequestTakeScreenshotTimestampMs != 0 @@ -1237,6 +1260,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final long identity = Binder.clearCallingIdentity(); try { final IBinder overlayWindowToken = new Binder(); + if (wmTracingEnabled()) { + logTraceWM("addWindowToken", + overlayWindowToken + ";TYPE_ACCESSIBILITY_OVERLAY;" + displayId + ";null"); + } mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */); synchronized (mLock) { @@ -1263,6 +1290,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ */ public void onDisplayRemoved(int displayId) { final long identity = Binder.clearCallingIdentity(); + if (wmTracingEnabled()) { + logTraceWM( + "addWindowToken", mOverlayWindowTokens.get(displayId) + ";true;" + displayId); + } try { mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true, displayId); @@ -1282,9 +1313,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ */ @Override public IBinder getOverlayWindowToken(int displayId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getOverlayWindowToken", - "displayId=" + displayId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getOverlayWindowToken", "displayId=" + displayId); } synchronized (mLock) { return mOverlayWindowTokens.get(displayId); @@ -1299,9 +1329,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ */ @Override public int getWindowIdForLeashToken(@NonNull IBinder token) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindowIdForLeashToken", - "token=" + token); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getWindowIdForLeashToken", "token=" + token); } synchronized (mLock) { return mA11yWindowManager.getWindowIdLocked(token); @@ -1314,8 +1343,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ // Clear the proxy in the other process so this // IAccessibilityServiceConnection can be garbage collected. if (mServiceInterface != null) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", "null, " + mId + ", null"); + if (svcClientTracingEnabled()) { + logTraceSvcClient("init", "null, " + mId + ", null"); } mServiceInterface.init(null, mId, null); } @@ -1465,9 +1494,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityEvent", - event + ";" + serviceWantsEvent); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent); } listener.onAccessibilityEvent(event, serviceWantsEvent); if (DEBUG) { @@ -1522,9 +1550,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onMagnificationChanged", displayId - + ", " + region + ", " + scale + ", " + centerX + ", " + centerY); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", " + + scale + ", " + centerX + ", " + centerY); } listener.onMagnificationChanged(displayId, region, scale, centerX, centerY); } catch (RemoteException re) { @@ -1541,9 +1569,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSoftKeyboardShowModeChanged", - String.valueOf(showState)); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState)); } listener.onSoftKeyboardShowModeChanged(showState); } catch (RemoteException re) { @@ -1557,9 +1584,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonClicked", - String.valueOf(displayId)); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId)); } listener.onAccessibilityButtonClicked(displayId); } catch (RemoteException re) { @@ -1579,9 +1605,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace( - TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonAvailabilityChanged", + if (svcClientTracingEnabled()) { + logTraceSvcClient("onAccessibilityButtonAvailabilityChanged", String.valueOf(available)); } listener.onAccessibilityButtonAvailabilityChanged(available); @@ -1597,9 +1622,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onGesture", - gestureInfo.toString()); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onGesture", gestureInfo.toString()); } listener.onGesture(gestureInfo); } catch (RemoteException re) { @@ -1613,8 +1637,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSystemActionsChanged"); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onSystemActionsChanged", ""); } listener.onSystemActionsChanged(); } catch (RemoteException re) { @@ -1628,8 +1652,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".clearAccessibilityCache"); + if (svcClientTracingEnabled()) { + logTraceSvcClient("clearAccessibilityCache", ""); } listener.clearAccessibilityCache(); } catch (RemoteException re) { @@ -1747,6 +1771,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ LocalServices.getService(ActivityTaskManagerInternal.class) .setFocusedActivity(activityToken); } + if (intConnTracingEnabled()) { + logTraceIntConn("performAccessibilityAction", + accessibilityNodeId + ";" + action + ";" + arguments + ";" + interactionId + + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + ";" + + interrogatingTid); + } connection.getRemote().performAccessibilityAction(accessibilityNodeId, action, arguments, interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid); @@ -1957,8 +1987,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setGestureDetectionPassthroughRegion(int displayId, Region region) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setGestureDetectionPassthroughRegion", + if (svcConnTracingEnabled()) { + logTraceSvcConn("setGestureDetectionPassthroughRegion", "displayId=" + displayId + ";region=" + region); } mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region); @@ -1966,8 +1996,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setTouchExplorationPassthroughRegion(int displayId, Region region) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setTouchExplorationPassthroughRegion", + if (svcConnTracingEnabled()) { + logTraceSvcConn("setTouchExplorationPassthroughRegion", "displayId=" + displayId + ";region=" + region); } mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region); @@ -1975,20 +2005,56 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Override public void setFocusAppearance(int strokeWidth, int color) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setFocusAppearance", - "strokeWidth=" + strokeWidth + ";color=" + color); + if (svcConnTracingEnabled()) { + logTraceSvcConn("setFocusAppearance", "strokeWidth=" + strokeWidth + ";color=" + color); } } @Override - public void logTrace(long timestamp, String where, String callingParams, int processId, - long threadId, int callingUid, Bundle callingStack) { - if (mTrace.isA11yTracingEnabled()) { + public void logTrace(long timestamp, String where, long loggingTypes, String callingParams, + int processId, long threadId, int callingUid, Bundle callingStack) { + if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) { ArrayList<StackTraceElement> list = (ArrayList<StackTraceElement>) callingStack.getSerializable(CALL_STACK); - mTrace.logTrace(timestamp, where, callingParams, processId, threadId, callingUid, - list.toArray(new StackTraceElement[list.size()])); + HashSet<String> ignoreList = + (HashSet<String>) callingStack.getSerializable(IGNORE_CALL_STACK); + mTrace.logTrace(timestamp, where, loggingTypes, callingParams, processId, threadId, + callingUid, list.toArray(new StackTraceElement[list.size()]), ignoreList); } } + + protected boolean svcClientTracingEnabled() { + return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT); + } + + protected void logTraceSvcClient(String methodName, String params) { + mTrace.logTrace(TRACE_SVC_CLIENT + "." + methodName, + FLAGS_ACCESSIBILITY_SERVICE_CLIENT, params); + } + + protected boolean svcConnTracingEnabled() { + return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CONNECTION); + } + + protected void logTraceSvcConn(String methodName, String params) { + mTrace.logTrace(TRACE_SVC_CONN + "." + methodName, + FLAGS_ACCESSIBILITY_SERVICE_CONNECTION, params); + } + + protected boolean intConnTracingEnabled() { + return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION); + } + + protected void logTraceIntConn(String methodName, String params) { + mTrace.logTrace(LOG_TAG + "." + methodName, + FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION, params); + } + + protected boolean wmTracingEnabled() { + return mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL); + } + + protected void logTraceWM(String methodName, String params) { + mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params); + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 7403af7605bc..bae2e1ac42ae 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -19,6 +19,7 @@ package com.android.server.accessibility; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; +import android.accessibilityservice.AccessibilityTrace; import android.annotation.MainThread; import android.content.Context; import android.graphics.Region; @@ -224,7 +225,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo Slog.d(TAG, "Received event: " + event + ", policyFlags=0x" + Integer.toHexString(policyFlags)); } - + if (mAms.getTraceManager().isA11yTracingEnabledForTypes( + AccessibilityTrace.FLAGS_INPUT_FILTER)) { + mAms.getTraceManager().logTrace(TAG + ".onInputEvent", + AccessibilityTrace.FLAGS_INPUT_FILTER, + "event=" + event + ";policyFlags=" + policyFlags); + } if (mEventHandler.size() == 0) { if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event); super.onInputEvent(event, policyFlags); @@ -424,7 +430,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo final ArrayList<Display> displaysList = mAms.getValidDisplayList(); if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) { - mAutoclickController = new AutoclickController(mContext, mUserId); + mAutoclickController = new AutoclickController( + mContext, mUserId, mAms.getTraceManager()); addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController); } @@ -462,7 +469,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { MotionEventInjector injector = new MotionEventInjector( - mContext.getMainLooper()); + mContext.getMainLooper(), mAms.getTraceManager()); addFirstEventHandler(displayId, injector); mMotionEventInjectors.put(displayId, injector); } @@ -563,15 +570,15 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo final Context uiContext = displayContext.createWindowContext( TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */); magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext, - mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(), - detectControlGestures, triggerable, + mAms.getWindowMagnificationMgr(), mAms.getTraceManager(), + mAms.getMagnificationController(), detectControlGestures, triggerable, displayId); } else { final Context uiContext = displayContext.createWindowContext( TYPE_MAGNIFICATION_OVERLAY, null /* options */); magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext, - mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(), - detectControlGestures, triggerable, + mAms.getFullScreenMagnificationController(), mAms.getTraceManager(), + mAms.getMagnificationController(), detectControlGestures, triggerable, new WindowMagnificationPromptController(displayContext, mUserId), displayId); } return magnificationGestureHandler; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index f63198866b08..5c7d0ae5ead5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -16,6 +16,15 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_FINGERPRINT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROADCAST_RECEIVER; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; @@ -289,8 +298,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext = context; mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); - mTraceManager = new AccessibilityTraceManager( - mWindowManagerService.getAccessibilityController(), this); + mTraceManager = AccessibilityTraceManager.getInstance( + mWindowManagerService.getAccessibilityController(), this, mLock); mMainHandler = new MainHandler(mContext.getMainLooper()); mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); mPackageManager = packageManager; @@ -311,8 +320,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext = context; mPowerManager = context.getSystemService(PowerManager.class); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); - mTraceManager = new AccessibilityTraceManager( - mWindowManagerService.getAccessibilityController(), this); + mTraceManager = AccessibilityTraceManager.getInstance( + mWindowManagerService.getAccessibilityController(), this, mLock); mMainHandler = new MainHandler(mContext.getMainLooper()); mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); mPackageManager = mContext.getPackageManager(); @@ -324,7 +333,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext, this); mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, - mWindowManagerService, this, mSecurityPolicy, this); + mWindowManagerService, this, mSecurityPolicy, this, mTraceManager); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); mMagnificationController = new MagnificationController(this, mLock, mContext); init(); @@ -339,26 +348,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public int getCurrentUserIdLocked() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getCurrentUserIdLocked"); - } return mCurrentUserId; } @Override public boolean isAccessibilityButtonShown() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".isAccessibilityButtonShown"); - } return mIsAccessibilityButtonShown; } @Override public void onServiceInfoChangedLocked(AccessibilityUserState userState) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace( - LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState); - } mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId, userState.mBoundServices); scheduleNotifyClientsOfServicesStateChangeLocked(userState); @@ -424,8 +423,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub PackageMonitor monitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged"); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { + mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged", + FLAGS_PACKAGE_BROADCAST_RECEIVER); } synchronized (mLock) { @@ -452,8 +452,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // mBindingServices in binderDied() during updating. Remove services from this // package from mBindingServices, and then update the user state to re-bind new // versions of them. - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished", + FLAGS_PACKAGE_BROADCAST_RECEIVER, "packageName=" + packageName + ";uid=" + uid); } synchronized (mLock) { @@ -485,8 +486,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onPackageRemoved(String packageName, int uid) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved", + FLAGS_PACKAGE_BROADCAST_RECEIVER, "packageName=" + packageName + ";uid=" + uid); } @@ -529,8 +531,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) { mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop", + FLAGS_PACKAGE_BROADCAST_RECEIVER, "intent=" + intent + ";packages=" + packages + ";uid=" + uid + ";doit=" + doit); } @@ -580,8 +583,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) { + mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER, "context=" + context + ";intent=" + intent); } @@ -668,8 +671,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public long addClient(IAccessibilityManagerClient callback, int userId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".addClient", + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".addClient", FLAGS_ACCESSIBILITY_MANAGER, "callback=" + callback + ";userId=" + userId); } @@ -739,8 +742,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void sendAccessibilityEvent(AccessibilityEvent event, int userId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", FLAGS_ACCESSIBILITY_MANAGER, "event=" + event + ";userId=" + userId); } boolean dispatchEvent = false; @@ -803,6 +806,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } if (shouldComputeWindows) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) { + mTraceManager.logTrace("WindowManagerInternal.computeWindowsForAccessibility", + FLAGS_WINDOW_MANAGER_INTERNAL, "display=" + displayId); + } final WindowManagerInternal wm = LocalServices.getService( WindowManagerInternal.class); wm.computeWindowsForAccessibility(displayId); @@ -835,9 +842,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void registerSystemAction(RemoteAction action, int actionId) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".registerSystemAction", - "action=" + action + ";actionId=" + actionId); + FLAGS_ACCESSIBILITY_MANAGER, "action=" + action + ";actionId=" + actionId); } mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); getSystemActionPerformer().registerSystemAction(actionId, action); @@ -850,8 +857,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void unregisterSystemAction(int actionId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", + FLAGS_ACCESSIBILITY_MANAGER, "actionId=" + actionId); } mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); getSystemActionPerformer().unregisterSystemAction(actionId); @@ -867,9 +875,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList", - "userId=" + userId); + FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId); } final int resolvedUserId; @@ -903,8 +911,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList", + FLAGS_ACCESSIBILITY_MANAGER, "feedbackType=" + feedbackType + ";userId=" + userId); } @@ -936,8 +945,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void interrupt(int userId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".interrupt", "userId=" + userId); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".interrupt", + FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId); } List<IAccessibilityServiceClient> interfacesToInterrupt; @@ -966,8 +976,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) { try { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt"); + if (mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { + mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt", + FLAGS_ACCESSIBILITY_SERVICE_CLIENT); } interfacesToInterrupt.get(i).onInterrupt(); } catch (RemoteException re) { @@ -981,8 +993,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken, IAccessibilityInteractionConnection connection, String packageName, int userId) throws RemoteException { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".addAccessibilityInteractionConnection", + FLAGS_ACCESSIBILITY_MANAGER, "windowToken=" + windowToken + "leashToken=" + leashToken + ";connection=" + connection + "; packageName=" + packageName + ";userId=" + userId); } @@ -993,9 +1006,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void removeAccessibilityInteractionConnection(IWindow window) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection", - "window=" + window); + FLAGS_ACCESSIBILITY_MANAGER, "window=" + window); } mA11yWindowManager.removeAccessibilityInteractionConnection(window); } @@ -1003,9 +1016,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void setPictureInPictureActionReplacingConnection( IAccessibilityInteractionConnection connection) throws RemoteException { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection", - "connection=" + connection); + FLAGS_ACCESSIBILITY_MANAGER, "connection=" + connection); } mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA, SET_PIP_ACTION_REPLACEMENT); @@ -1017,10 +1030,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub IAccessibilityServiceClient serviceClient, AccessibilityServiceInfo accessibilityServiceInfo, int flags) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner - + ";serviceClient=" + serviceClient + ";accessibilityServiceInfo=" - + accessibilityServiceInfo + ";flags=" + flags); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", + FLAGS_ACCESSIBILITY_MANAGER, + "owner=" + owner + ";serviceClient=" + serviceClient + + ";accessibilityServiceInfo=" + accessibilityServiceInfo + ";flags=" + flags); } mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, @@ -1037,9 +1051,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".unregisterUiTestAutomationService", - "serviceClient=" + serviceClient); + FLAGS_ACCESSIBILITY_MANAGER, "serviceClient=" + serviceClient); } synchronized (mLock) { mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient); @@ -1049,15 +1063,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void temporaryEnableAccessibilityStateUntilKeyguardRemoved( ComponentName service, boolean touchExplorationEnabled) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace( LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved", + FLAGS_ACCESSIBILITY_MANAGER, "service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled); } mSecurityPolicy.enforceCallingPermission( Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY, TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) { + mTraceManager.logTrace("WindowManagerInternal.isKeyguardLocked", + FLAGS_WINDOW_MANAGER_INTERNAL); + } if (!mWindowManagerService.isKeyguardLocked()) { return; } @@ -1083,9 +1102,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public IBinder getWindowToken(int windowId, int userId) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".getWindowToken", - "windowId=" + windowId + ";userId=" + userId); + FLAGS_ACCESSIBILITY_MANAGER, "windowId=" + windowId + ";userId=" + userId); } mSecurityPolicy.enforceCallingPermission( @@ -1127,8 +1146,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void notifyAccessibilityButtonClicked(int displayId, String targetName) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked", + FLAGS_ACCESSIBILITY_MANAGER, "displayId=" + displayId + ";targetName=" + targetName); } @@ -1157,9 +1177,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged", - "shown=" + shown); + FLAGS_ACCESSIBILITY_MANAGER, "shown=" + shown); } mSecurityPolicy.enforceCallingOrSelfPermission( @@ -1190,10 +1210,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void onSystemActionsChanged() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged"); - } - synchronized (mLock) { AccessibilityUserState state = getCurrentUserStateLocked(); notifySystemActionsChangedLocked(state); @@ -1256,11 +1272,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked", - "displayId=" + displayId); - } - final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS; MotionEventInjector motionEventInjector = null; while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) { @@ -1323,6 +1334,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { token = getWindowToken(windowId, mCurrentUserId); } + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) { + mTraceManager.logTrace("WindowManagerInternal.getWindowFrame", + FLAGS_WINDOW_MANAGER_INTERNAL, "token=" + token + ";outBounds=" + outBounds); + } mWindowManagerService.getWindowFrame(token, outBounds); if (!outBounds.isEmpty()) { return true; @@ -1471,7 +1486,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private int getClientStateLocked(AccessibilityUserState userState) { return userState.getClientStateLocked( mUiAutomationManager.isUiAutomationRunningLocked(), - mTraceManager.isA11yTracingEnabled()); + mTraceManager.getTraceStateForAccessibilityManagerClientState()); } private InteractionBridge getInteractionBridge() { @@ -1681,6 +1696,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void updateRelevantEventsLocked(AccessibilityUserState userState) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { + mTraceManager.logTrace(LOG_TAG + ".updateRelevantEventsLocked", + FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState); + } mMainHandler.post(() -> { broadcastToClients(userState, ignoreRemoteException(client -> { int relevantEventTypes; @@ -1830,12 +1849,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void persistComponentNamesToSettingLocked(String settingName, Set<ComponentName> componentNames, int userId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked", - "settingName=" + settingName + ";componentNames=" + componentNames + ";userId=" - + userId); - } - persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames, componentName -> componentName.flattenToShortString()); } @@ -1960,7 +1973,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) { + void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) { final int clientState = getClientStateLocked(userState); if (userState.getLastSentClientStateLocked() != clientState && (mGlobalClients.getRegisteredCallbackCount() > 0 @@ -1983,6 +1996,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void sendStateToClients(int clientState, RemoteCallbackList<IAccessibilityManagerClient> clients) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) { + mTraceManager.logTrace(LOG_TAG + ".sendStateToClients", + FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState); + } clients.broadcast(ignoreRemoteException( client -> client.setState(clientState))); } @@ -2003,6 +2020,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyClientsOfServicesStateChange( RemoteCallbackList<IAccessibilityManagerClient> clients, long uiTimeout) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) { + mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange", + FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout); + } clients.broadcast(ignoreRemoteException( client -> client.notifyServicesStateChanged(uiTimeout))); } @@ -2082,6 +2103,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } if (setInputFilter) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL + | FLAGS_INPUT_FILTER)) { + mTraceManager.logTrace("WindowManagerInternal.setInputFilter", + FLAGS_WINDOW_MANAGER_INTERNAL | FLAGS_INPUT_FILTER, + "inputFilter=" + inputFilter); + } mWindowManagerService.setInputFilter(inputFilter); } } @@ -2805,26 +2832,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @GuardedBy("mLock") @Override public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked", - "windowId=" + windowId); - } - IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( mCurrentUserId, windowId); if (windowToken != null) { - return mWindowManagerService.getCompatibleMagnificationSpecForWindow( - windowToken); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) { + mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow", + FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken); + } + + return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken); } return null; } @Override public KeyEventDispatcher getKeyEventDispatcher() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getKeyEventDispatcher"); - } - if (mKeyEventDispatcher == null) { mKeyEventDispatcher = new KeyEventDispatcher( mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock, @@ -2837,13 +2859,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent, int flags) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getPendingIntentActivity", - "context=" + context + ";requestCode=" + requestCode + ";intent=" + intent - + ";flags=" + flags); - } - - return PendingIntent.getActivity(context, requestCode, intent, flags); } @@ -2858,9 +2873,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public void performAccessibilityShortcut(String targetName) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut", - "targetName=" + targetName); + FLAGS_ACCESSIBILITY_MANAGER, "targetName=" + targetName); } if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) @@ -3048,9 +3063,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets", - "shortcutType=" + shortcutType); + FLAGS_ACCESSIBILITY_MANAGER, "shortcutType=" + shortcutType); } if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY) @@ -3122,11 +3137,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked", - "event=" + event); - } - sendAccessibilityEventLocked(event, mCurrentUserId); } @@ -3148,8 +3158,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public boolean sendFingerprintGesture(int gestureKeyCode) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT)) { mTraceManager.logTrace(LOG_TAG + ".sendFingerprintGesture", + FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT, "gestureKeyCode=" + gestureKeyCode); } @@ -3174,9 +3186,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public int getAccessibilityWindowId(@Nullable IBinder windowToken) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".getAccessibilityWindowId", - "windowToken=" + windowToken); + FLAGS_ACCESSIBILITY_MANAGER, "windowToken=" + windowToken); } synchronized (mLock) { @@ -3196,8 +3208,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ @Override public long getRecommendedTimeoutMillis() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getRecommendedTimeoutMillis"); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace( + LOG_TAG + ".getRecommendedTimeoutMillis", FLAGS_ACCESSIBILITY_MANAGER); } synchronized(mLock) { @@ -3214,8 +3227,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void setWindowMagnificationConnection( IWindowMagnificationConnection connection) throws RemoteException { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection", + FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connection=" + connection); } @@ -3249,9 +3264,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) { - if (mTraceManager.isA11yTracingEnabled()) { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { mTraceManager.logTrace(LOG_TAG + ".associateEmbeddedHierarchy", - "host=" + host + ";embedded=" + embedded); + FLAGS_ACCESSIBILITY_MANAGER, "host=" + host + ";embedded=" + embedded); } synchronized (mLock) { @@ -3261,8 +3276,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void disassociateEmbeddedHierarchy(@NonNull IBinder token) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token); + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", + FLAGS_ACCESSIBILITY_MANAGER, "token=" + token); } synchronized (mLock) { @@ -3274,7 +3290,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Gets the stroke width of the focus rectangle. * @return The stroke width. */ + @Override public int getFocusStrokeWidth() { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER); + } synchronized (mLock) { final AccessibilityUserState userState = getCurrentUserStateLocked(); @@ -3286,7 +3306,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Gets the color of the focus rectangle. * @return The color. */ + @Override public int getFocusColor() { + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) { + mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER); + } synchronized (mLock) { final AccessibilityUserState userState = getCurrentUserStateLocked(); @@ -3350,9 +3374,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public FullScreenMagnificationController getFullScreenMagnificationController() { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController"); - } synchronized (mLock) { return mMagnificationController.getFullScreenMagnificationController(); } @@ -3360,11 +3381,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onClientChangeLocked(boolean serviceInfoChanged) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked", - "serviceInfoChanged=" + serviceInfoChanged); - } - AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); onUserStateChangedLocked(userState); if (serviceInfoChanged) { @@ -3891,11 +3907,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void setGestureDetectionPassthroughRegion(int displayId, Region region) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion", - "displayId=" + displayId + ";region=" + region); - } - mMainHandler.sendMessage( obtainMessage( AccessibilityManagerService::setGestureDetectionPassthroughRegionInternal, @@ -3906,11 +3917,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void setTouchExplorationPassthroughRegion(int displayId, Region region) { - if (mTraceManager.isA11yTracingEnabled()) { - mTraceManager.logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion", - "displayId=" + displayId + ";region=" + region); - } - mMainHandler.sendMessage( obtainMessage( AccessibilityManagerService::setTouchExplorationPassthroughRegionInternal, @@ -3939,7 +3945,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userState.mUserId != mCurrentUserId) { return; } - + if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { + mTraceManager.logTrace(LOG_TAG + ".updateFocusAppearanceDataLocked", + FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState); + } mMainHandler.post(() -> { broadcastToClients(userState, ignoreRemoteException(client -> { client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(), @@ -3949,7 +3958,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } - AccessibilityTraceManager getTraceManager() { + public AccessibilityTraceManager getTraceManager() { return mTraceManager; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 7d75b738d818..467cab5fec04 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.Manifest; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; import android.app.PendingIntent; import android.content.ComponentName; @@ -53,10 +54,7 @@ import java.util.Set; */ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection { private static final String LOG_TAG = "AccessibilityServiceConnection"; - private static final String TRACE_A11Y_SERVICE_CONNECTION = - LOG_TAG + ".IAccessibilityServiceConnection"; - private static final String TRACE_A11Y_SERVICE_CLIENT = - LOG_TAG + ".IAccessibilityServiceClient"; + /* Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps lists of bound and binding services. These are freed on user changes, but just in case it @@ -137,8 +135,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void disableSelf() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".disableSelf"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("disableSelf", ""); } synchronized (mLock) { AccessibilityUserState userState = mUserStateWeakReference.get(); @@ -218,9 +216,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return; } try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", this + ", " + mId + ", " - + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); + if (svcClientTracingEnabled()) { + logTraceSvcClient("init", + this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } catch (RemoteException re) { @@ -264,9 +262,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public boolean setSoftKeyboardShowMode(int showMode) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardShowMode", - "showMode=" + showMode); + if (svcConnTracingEnabled()) { + logTraceSvcConn("setSoftKeyboardShowMode", "showMode=" + showMode); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -280,8 +277,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public int getSoftKeyboardShowMode() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSoftKeyboardShowMode"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("getSoftKeyboardShowMode", ""); } final AccessibilityUserState userState = mUserStateWeakReference.get(); return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0; @@ -289,9 +286,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public boolean switchToInputMethod(String imeId) { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".switchToInputMethod", - "imeId=" + imeId); + if (svcConnTracingEnabled()) { + logTraceSvcConn("switchToInputMethod", "imeId=" + imeId); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -311,8 +307,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public boolean isAccessibilityButtonAvailable() { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".isAccessibilityButtonAvailable"); + if (svcConnTracingEnabled()) { + logTraceSvcConn("isAccessibilityButtonAvailable", ""); } synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { @@ -373,9 +369,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } if (serviceInterface != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT - + ".onFingerprintCapturingGesturesChanged", String.valueOf(active)); + if (svcClientTracingEnabled()) { + logTraceSvcClient( + "onFingerprintCapturingGesturesChanged", String.valueOf(active)); } mServiceInterface.onFingerprintCapturingGesturesChanged(active); } catch (RemoteException e) { @@ -394,9 +390,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } if (serviceInterface != null) { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onFingerprintGesture", - String.valueOf(gesture)); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture)); } mServiceInterface.onFingerprintGesture(gesture); } catch (RemoteException e) { @@ -410,15 +405,17 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (mSecurityPolicy.canPerformGestures(this)) { MotionEventInjector motionEventInjector = mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId); + if (wmTracingEnabled()) { + logTraceWM("isTouchOrFaketouchDevice", ""); + } if (motionEventInjector != null && mWindowManagerService.isTouchOrFaketouchDevice()) { motionEventInjector.injectEvents( gestureSteps.getList(), mServiceInterface, sequence, displayId); } else { try { - if (mTrace.isA11yTracingEnabled()) { - mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onPerformGestureResult", - sequence + ", false"); + if (svcClientTracingEnabled()) { + logTraceSvcClient("onPerformGestureResult", sequence + ", false"); } mServiceInterface.onPerformGestureResult(sequence, false); } catch (RemoteException re) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java index 6396960281b7..8cf5547b05ec 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java @@ -60,7 +60,7 @@ final class AccessibilityShellCommand extends ShellCommand { } case "start-trace": case "stop-trace": - return mService.getTraceManager().onShellCommand(cmd); + return mService.getTraceManager().onShellCommand(cmd, this); } return -1; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java deleted file mode 100644 index 03914138cd42..000000000000 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.accessibility; - -/** - * Interface to log accessibility trace. - */ -public interface AccessibilityTrace { - /** - * Whether the trace is enabled. - */ - boolean isA11yTracingEnabled(); - - /** - * Start tracing. - */ - void startTrace(); - - /** - * Stop tracing. - */ - void stopTrace(); - - /** - * Log one trace entry. - * @param where A string to identify this log entry, which can be used to filter/search - * through the tracing file. - */ - void logTrace(String where); - - /** - * Log one trace entry. - * @param where A string to identify this log entry, which can be used to filter/search - * through the tracing file. - * @param callingParams The parameters for the method to be logged. - */ - void logTrace(String where, String callingParams); - - /** - * Log one trace entry. Accessibility services using AccessibilityInteractionClient to - * make screen content related requests use this API to log entry when receive callback. - * @param timestamp The timestamp when a callback is received. - * @param where A string to identify this log entry, which can be used to filter/search - * through the tracing file. - * @param callingParams The parameters for the callback. - * @param processId The process id of the calling component. - * @param threadId The threadId of the calling component. - * @param callingUid The calling uid of the callback. - * @param callStack The call stack of the callback. - */ - void logTrace(long timestamp, String where, String callingParams, int processId, - long threadId, int callingUid, StackTraceElement[] callStack); -} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java index 6105e8a6724b..51e01ea58a35 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java @@ -15,72 +15,197 @@ */ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_ALL; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_NONE; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED; + +import android.accessibilityservice.AccessibilityTrace; +import android.annotation.MainThread; import android.annotation.NonNull; import android.os.Binder; +import android.os.ShellCommand; import com.android.server.wm.WindowManagerInternal; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * Manager of accessibility trace. */ -class AccessibilityTraceManager implements AccessibilityTrace { +public class AccessibilityTraceManager implements AccessibilityTrace { private final WindowManagerInternal.AccessibilityControllerInternal mA11yController; private final AccessibilityManagerService mService; + private final Object mA11yMSLock; + + private long mEnabledLoggingFlags; + + private static AccessibilityTraceManager sInstance = null; + + @MainThread + static AccessibilityTraceManager getInstance( + @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController, + @NonNull AccessibilityManagerService service, + @NonNull Object lock) { + if (sInstance == null) { + sInstance = new AccessibilityTraceManager(a11yController, service, lock); + } + return sInstance; + } - AccessibilityTraceManager( + private AccessibilityTraceManager( @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController, - @NonNull AccessibilityManagerService service) { + @NonNull AccessibilityManagerService service, + @NonNull Object lock) { mA11yController = a11yController; mService = service; + mA11yMSLock = lock; + mEnabledLoggingFlags = FLAGS_LOGGING_NONE; } @Override public boolean isA11yTracingEnabled() { - return mA11yController.isAccessibilityTracingEnabled(); + synchronized (mA11yMSLock) { + return mEnabledLoggingFlags != FLAGS_LOGGING_NONE; + } + } + + @Override + public boolean isA11yTracingEnabledForTypes(long typeIdFlags) { + synchronized (mA11yMSLock) { + return ((typeIdFlags & mEnabledLoggingFlags) != FLAGS_LOGGING_NONE); + } + } + + @Override + public int getTraceStateForAccessibilityManagerClientState() { + int state = 0x0; + synchronized (mA11yMSLock) { + if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION)) { + state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED; + } + if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK)) { + state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED; + } + if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CLIENT)) { + state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED; + } + if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE)) { + state |= STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED; + } + } + return state; } @Override - public void startTrace() { - if (!mA11yController.isAccessibilityTracingEnabled()) { - mA11yController.startTrace(); - mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState()); + public void startTrace(long loggingTypes) { + if (loggingTypes == FLAGS_LOGGING_NONE) { + // Ignore start none request + return; + } + + synchronized (mA11yMSLock) { + long oldEnabled = mEnabledLoggingFlags; + mEnabledLoggingFlags = loggingTypes; + + if (needToNotifyClients(oldEnabled)) { + mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState()); + } } + + mA11yController.startTrace(loggingTypes); } @Override public void stopTrace() { - if (mA11yController.isAccessibilityTracingEnabled()) { + boolean stop = false; + synchronized (mA11yMSLock) { + stop = isA11yTracingEnabled(); + + long oldEnabled = mEnabledLoggingFlags; + mEnabledLoggingFlags = FLAGS_LOGGING_NONE; + + if (needToNotifyClients(oldEnabled)) { + mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState()); + } + } + if (stop) { mA11yController.stopTrace(); - mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState()); } } @Override - public void logTrace(String where) { - logTrace(where, ""); + public void logTrace(String where, long loggingTypes) { + logTrace(where, loggingTypes, ""); } @Override - public void logTrace(String where, String callingParams) { - mA11yController.logTrace(where, callingParams, "".getBytes(), - Binder.getCallingUid(), Thread.currentThread().getStackTrace()); + public void logTrace(String where, long loggingTypes, String callingParams) { + if (isA11yTracingEnabledForTypes(loggingTypes)) { + mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(), + Binder.getCallingUid(), Thread.currentThread().getStackTrace(), + new HashSet<String>(Arrays.asList("logTrace"))); + } } @Override - public void logTrace(long timestamp, String where, String callingParams, int processId, - long threadId, int callingUid, StackTraceElement[] callStack) { - if (mA11yController.isAccessibilityTracingEnabled()) { - mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack, - timestamp, processId, threadId); + public void logTrace(long timestamp, String where, long loggingTypes, String callingParams, + int processId, long threadId, int callingUid, StackTraceElement[] callStack, + Set<String> ignoreElementList) { + if (isA11yTracingEnabledForTypes(loggingTypes)) { + mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(), callingUid, + callStack, timestamp, processId, threadId, + ((ignoreElementList == null) ? new HashSet<String>() : ignoreElementList)); } } - int onShellCommand(String cmd) { + private boolean needToNotifyClients(long otherTypesEnabled) { + return (mEnabledLoggingFlags & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES) + != (otherTypesEnabled & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES); + } + + int onShellCommand(String cmd, ShellCommand shell) { switch (cmd) { case "start-trace": { - startTrace(); + String opt = shell.getNextOption(); + if (opt == null) { + startTrace(FLAGS_LOGGING_ALL); + return 0; + } + List<String> types = new ArrayList<String>(); + while (opt != null) { + switch (opt) { + case "-t": { + String type = shell.getNextArg(); + while (type != null) { + types.add(type); + type = shell.getNextArg(); + } + break; + } + default: { + shell.getErrPrintWriter().println( + "Error: option not recognized " + opt); + stopTrace(); + return -1; + } + } + opt = shell.getNextOption(); + } + long enabledTypes = AccessibilityTrace.getLoggingFlagsFromNames(types); + startTrace(enabledTypes); return 0; } case "stop-trace": { @@ -92,8 +217,29 @@ class AccessibilityTraceManager implements AccessibilityTrace { } void onHelp(PrintWriter pw) { - pw.println(" start-trace"); - pw.println(" Start the debug tracing."); + pw.println(" start-trace [-t LOGGING_TYPE [LOGGING_TYPE...]]"); + pw.println(" Start the debug tracing. If no option is present, full trace will be"); + pw.println(" generated. Options are:"); + pw.println(" -t: Only generate tracing for the logging type(s) specified here."); + pw.println(" LOGGING_TYPE can be any one of below:"); + pw.println(" IAccessibilityServiceConnection"); + pw.println(" IAccessibilityServiceClient"); + pw.println(" IAccessibilityManager"); + pw.println(" IAccessibilityManagerClient"); + pw.println(" IAccessibilityInteractionConnection"); + pw.println(" IAccessibilityInteractionConnectionCallback"); + pw.println(" IRemoteMagnificationAnimationCallback"); + pw.println(" IWindowMagnificationConnection"); + pw.println(" IWindowMagnificationConnectionCallback"); + pw.println(" WindowManagerInternal"); + pw.println(" WindowsForAccessibilityCallback"); + pw.println(" MagnificationCallbacks"); + pw.println(" InputFilter"); + pw.println(" Gesture"); + pw.println(" AccessibilityService"); + pw.println(" PMBroadcastReceiver"); + pw.println(" UserBroadcastReceiver"); + pw.println(" FingerprintGesture"); pw.println(" stop-trace"); pw.println(" Stop the debug tracing."); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 0fde0de59c07..c70bf73fc7f5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -392,7 +392,7 @@ class AccessibilityUserState { return mBoundServices; } - int getClientStateLocked(boolean isUiAutomationRunning, boolean isTracingEnabled) { + int getClientStateLocked(boolean isUiAutomationRunning, int traceClientState) { int clientState = 0; final boolean a11yEnabled = isUiAutomationRunning || isHandlingAccessibilityEventsLocked(); @@ -408,9 +408,9 @@ class AccessibilityUserState { if (mIsTextHighContrastEnabled) { clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; } - if (isTracingEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED; - } + + clientState |= traceClientState; + return clientState; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index ff794691d2b4..b05dffef9c53 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; @@ -69,6 +71,7 @@ public class AccessibilityWindowManager { private final AccessibilityEventSender mAccessibilityEventSender; private final AccessibilitySecurityPolicy mSecurityPolicy; private final AccessibilityUserManager mAccessibilityUserManager; + private final AccessibilityTraceManager mTraceManager; // Connections and window tokens for cross-user windows private final SparseArray<RemoteAccessibilityConnection> @@ -151,6 +154,10 @@ public class AccessibilityWindowManager { // In some cases, onWindowsForAccessibilityChanged will be called immediately in // setWindowsForAccessibilityCallback. We'll lost windows if flag is false. mTrackingWindows = true; + if (traceWMEnabled()) { + logTraceWM("setWindowsForAccessibilityCallback", + "displayId=" + mDisplayId + ";callback=" + this); + } result = mWindowManagerInternal.setWindowsForAccessibilityCallback( mDisplayId, this); if (!result) { @@ -167,6 +174,10 @@ public class AccessibilityWindowManager { */ void stopTrackingWindowsLocked() { if (mTrackingWindows) { + if (traceWMEnabled()) { + logTraceWM("setWindowsForAccessibilityCallback", + "displayId=" + mDisplayId + ";callback=null"); + } mWindowManagerInternal.setWindowsForAccessibilityCallback( mDisplayId, null); mTrackingWindows = false; @@ -373,6 +384,20 @@ public class AccessibilityWindowManager { } } + /** + * Called when the display is reparented and becomes an embedded + * display. + * + * @param embeddedDisplayId The embedded display Id. + */ + @Override + public void onDisplayReparented(int embeddedDisplayId) { + // Removes the un-used window observer for the embedded display. + synchronized (mLock) { + mDisplayWindowsObservers.remove(embeddedDisplayId); + } + } + private boolean shouldUpdateWindowsLocked(boolean forceSend, @NonNull List<WindowInfo> windows) { if (forceSend) { @@ -844,19 +869,21 @@ public class AccessibilityWindowManager { } /** - * Constructor for AccessibilityManagerService. + * Constructor for AccessibilityWindowManager. */ public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler, @NonNull WindowManagerInternal windowManagerInternal, @NonNull AccessibilityEventSender accessibilityEventSender, @NonNull AccessibilitySecurityPolicy securityPolicy, - @NonNull AccessibilityUserManager accessibilityUserManager) { + @NonNull AccessibilityUserManager accessibilityUserManager, + @NonNull AccessibilityTraceManager traceManager) { mLock = lock; mHandler = handler; mWindowManagerInternal = windowManagerInternal; mAccessibilityEventSender = accessibilityEventSender; mSecurityPolicy = securityPolicy; mAccessibilityUserManager = accessibilityUserManager; + mTraceManager = traceManager; } /** @@ -957,6 +984,9 @@ public class AccessibilityWindowManager { final int windowId; boolean shouldComputeWindows = false; final IBinder token = window.asBinder(); + if (traceWMEnabled()) { + logTraceWM("getDisplayIdForWindow", "token=" + token); + } final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token); synchronized (mLock) { // We treat calls from a profile as if made by its parent as profiles @@ -1003,9 +1033,15 @@ public class AccessibilityWindowManager { registerIdLocked(leashToken, windowId); } if (shouldComputeWindows) { + if (traceWMEnabled()) { + logTraceWM("computeWindowsForAccessibility", "displayId=" + displayId); + } mWindowManagerInternal.computeWindowsForAccessibility(displayId); } - + if (traceWMEnabled()) { + logTraceWM("setAccessibilityIdToSurfaceMetadata", + "token=" + token + ";windowId=" + windowId); + } mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId); return windowId; } @@ -1139,6 +1175,10 @@ public class AccessibilityWindowManager { mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; } if (binder != null) { + if (traceWMEnabled()) { + logTraceWM("setAccessibilityIdToSurfaceMetadata", "token=" + binder + + ";windowId=AccessibilityWindowInfo.UNDEFINED_WINDOW_ID"); + } mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata( binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); } @@ -1169,6 +1209,9 @@ public class AccessibilityWindowManager { * @return The userId */ public int getWindowOwnerUserId(@NonNull IBinder windowToken) { + if (traceWMEnabled()) { + logTraceWM("getWindowOwnerUserId", "token=" + windowToken); + } return mWindowManagerInternal.getWindowOwnerUserId(windowToken); } @@ -1547,6 +1590,10 @@ public class AccessibilityWindowManager { for (int i = 0; i < connectionList.size(); i++) { final RemoteAccessibilityConnection connection = connectionList.get(i); if (connection != null) { + if (traceIntConnEnabled()) { + logTraceIntConn("notifyOutsideTouch"); + } + try { connection.getRemote().notifyOutsideTouch(); } catch (RemoteException re) { @@ -1567,6 +1614,9 @@ public class AccessibilityWindowManager { */ public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) { final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId); + if (traceWMEnabled()) { + logTraceWM("getDisplayIdForWindow", "token=" + windowToken); + } final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken); return displayId; } @@ -1595,6 +1645,9 @@ public class AccessibilityWindowManager { * @return The input focused windowId, or -1 if not found */ private int findFocusedWindowId(int userId) { + if (traceWMEnabled()) { + logTraceWM("getFocusedWindowToken", ""); + } final IBinder token = mWindowManagerInternal.getFocusedWindowToken(); synchronized (mLock) { return findWindowIdLocked(userId, token); @@ -1644,6 +1697,9 @@ public class AccessibilityWindowManager { return; } } + if (traceIntConnEnabled()) { + logTraceIntConn("notifyOutsideTouch"); + } try { connection.getRemote().clearAccessibilityFocus(); } catch (RemoteException re) { @@ -1666,6 +1722,25 @@ public class AccessibilityWindowManager { return null; } + private boolean traceWMEnabled() { + return mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL); + } + + private void logTraceWM(String methodName, String params) { + mTraceManager.logTrace("WindowManagerInternal." + methodName, + FLAGS_WINDOW_MANAGER_INTERNAL, params); + } + + private boolean traceIntConnEnabled() { + return mTraceManager.isA11yTracingEnabledForTypes( + FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION); + } + + private void logTraceIntConn(String methodName) { + mTraceManager.logTrace( + LOG_TAG + "." + methodName, FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION); + } + /** * Associate the token of the embedded view hierarchy to the host view hierarchy. * diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java index f5b0eb1eb5b6..95f3560dbbb8 100644 --- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java +++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java @@ -16,6 +16,7 @@ package com.android.server.accessibility; +import android.accessibilityservice.AccessibilityTrace; import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; @@ -56,6 +57,7 @@ public class AutoclickController extends BaseEventStreamTransformation { private static final String LOG_TAG = AutoclickController.class.getSimpleName(); + private final AccessibilityTraceManager mTrace; private final Context mContext; private final int mUserId; @@ -63,13 +65,18 @@ public class AutoclickController extends BaseEventStreamTransformation { private ClickScheduler mClickScheduler; private ClickDelayObserver mClickDelayObserver; - public AutoclickController(Context context, int userId) { + public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) { + mTrace = trace; mContext = context; mUserId = userId; } @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) { + mTrace.logTrace(LOG_TAG + ".onMotionEvent", AccessibilityTrace.FLAGS_INPUT_FILTER, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (mClickScheduler == null) { Handler handler = new Handler(mContext.getMainLooper()); @@ -89,6 +96,10 @@ public class AutoclickController extends BaseEventStreamTransformation { @Override public void onKeyEvent(KeyEvent event, int policyFlags) { + if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) { + mTrace.logTrace(LOG_TAG + ".onKeyEvent", AccessibilityTrace.FLAGS_INPUT_FILTER, + "event=" + event + ";policyFlags=" + policyFlags); + } if (mClickScheduler != null) { if (KeyEvent.isModifierKey(event.getKeyCode())) { mClickScheduler.updateMetaState(event.getMetaState()); diff --git a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java index bc379c204d44..b8250c028f62 100644 --- a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java +++ b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER; + import android.os.Handler; import android.os.Message; import android.os.SystemClock; @@ -64,6 +66,10 @@ public class KeyboardInterceptor extends BaseEventStreamTransformation implement @Override public void onKeyEvent(KeyEvent event, int policyFlags) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(FLAGS_INPUT_FILTER)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent", + FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags); + } /* * Certain keys have system-level behavior that affects accessibility services. * Let that behavior settle before handling the keys diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java index 2673cd1ac3b8..5cbd1a208ce1 100644 --- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java +++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java @@ -16,6 +16,7 @@ package com.android.server.accessibility; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.GestureDescription; import android.accessibilityservice.GestureDescription.GestureStep; import android.accessibilityservice.GestureDescription.TouchPoint; @@ -68,6 +69,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement private final Handler mHandler; private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>(); + private final AccessibilityTraceManager mTrace; private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture; private IntArray mSequencesInProgress = new IntArray(5); private boolean mIsDestroyed = false; @@ -80,15 +82,17 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement /** * @param looper A looper on the main thread to use for dispatching new events */ - public MotionEventInjector(Looper looper) { + public MotionEventInjector(Looper looper, AccessibilityTraceManager trace) { mHandler = new Handler(looper, this); + mTrace = trace; } /** * @param handler A handler to post messages. Exposes internal state for testing only. */ - public MotionEventInjector(Handler handler) { + public MotionEventInjector(Handler handler, AccessibilityTraceManager trace) { mHandler = handler; + mTrace = trace; } /** @@ -112,6 +116,12 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mTrace.isA11yTracingEnabledForTypes( + AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) { + mTrace.logTrace(LOG_TAG + ".onMotionEvent", + AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } // MotionEventInjector would cancel any injected gesture when any MotionEvent arrives. // For user using an external device to control the pointer movement, it's almost // impossible to perform the gestures. Any slightly unintended movement results in the diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 9547280018e4..6cd23fcfd0fb 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -17,6 +17,7 @@ package com.android.server.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; import android.annotation.Nullable; import android.app.UiAutomation; @@ -269,6 +270,14 @@ class UiAutomationManager { // If the serviceInterface is null, the UiAutomation has been shut down on // another thread. if (serviceInterface != null) { + if (mTrace.isA11yTracingEnabledForTypes( + AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) { + mTrace.logTrace("UiAutomationService.connectServiceUnknownThread", + AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT, + "serviceConnection=" + this + ";connectionId=" + mId + + "windowToken=" + + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); + } serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index e1af2c48789f..f95de4e8be8e 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -16,6 +16,8 @@ package com.android.server.accessibility.gestures; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_GESTURE; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_HOVER_ENTER; @@ -82,6 +84,7 @@ public class TouchExplorer extends BaseEventStreamTransformation implements GestureManifold.Listener { static final boolean DEBUG = false; + private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER; // Tag for logging received events. private static final String LOG_TAG = "TouchExplorer"; @@ -254,6 +257,10 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onMotionEvent", LOGGING_FLAGS, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { super.onMotionEvent(event, rawEvent, policyFlags); return; @@ -303,6 +310,10 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void onAccessibilityEvent(AccessibilityEvent event) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onAccessibilityEvent", + LOGGING_FLAGS, "event=" + event); + } final int eventType = event.getEventType(); if (eventType == TYPE_VIEW_HOVER_EXIT) { @@ -341,6 +352,10 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTapAndHold", LOGGING_FLAGS, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) { sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); if (isSendMotionEventsEnabled()) { @@ -357,6 +372,10 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTap", LOGGING_FLAGS, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } mAms.onTouchInteractionEnd(); // Remove pending event deliveries. mSendHoverEnterAndMoveDelayed.cancel(); @@ -389,6 +408,9 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onGestureStarted() { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureStarted", LOGGING_FLAGS); + } // We have to perform gesture detection, so // clear the current state and try to detect. mSendHoverEnterAndMoveDelayed.cancel(); @@ -402,6 +424,10 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCompleted", + LOGGING_FLAGS, "event=" + gestureEvent); + } endGestureDetection(true); mSendTouchInteractionEndDelayed.cancel(); dispatchGesture(gestureEvent); @@ -410,6 +436,10 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) { + mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCancelled", LOGGING_FLAGS, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } if (mState.isGestureDetecting()) { endGestureDetection(event.getActionMasked() == ACTION_UP); return true; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index 1f66bfdb20c1..718da9e390ab 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -16,6 +16,8 @@ package com.android.server.accessibility.magnification; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL; + import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.NonNull; @@ -46,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.wm.WindowManagerInternal; import java.util.Locale; @@ -135,6 +138,10 @@ public class FullScreenMagnificationController { */ @GuardedBy("mLock") boolean register() { + if (traceEnabled()) { + logTrace("setMagnificationCallbacks", + "displayID=" + mDisplayId + ";callback=" + this); + } mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks( mDisplayId, this); if (!mRegistered) { @@ -142,6 +149,10 @@ public class FullScreenMagnificationController { return false; } mSpecAnimationBridge.setEnabled(true); + if (traceEnabled()) { + logTrace("getMagnificationRegion", + "displayID=" + mDisplayId + ";region=" + mMagnificationRegion); + } // Obtain initial state. mControllerCtx.getWindowManager().getMagnificationRegion( mDisplayId, mMagnificationRegion); @@ -162,6 +173,10 @@ public class FullScreenMagnificationController { void unregister(boolean delete) { if (mRegistered) { mSpecAnimationBridge.setEnabled(false); + if (traceEnabled()) { + logTrace("setMagnificationCallbacks", + "displayID=" + mDisplayId + ";callback=null"); + } mControllerCtx.getWindowManager().setMagnificationCallbacks( mDisplayId, null); mMagnificationRegion.setEmpty(); @@ -268,7 +283,7 @@ public class FullScreenMagnificationController { } @Override - public void onRotationChanged(int rotation) { + public void onDisplaySizeChanged() { // Treat as context change and reset final Message m = PooledLambda.obtainMessage( FullScreenMagnificationController::resetIfNeeded, @@ -431,6 +446,10 @@ public class FullScreenMagnificationController { void setForceShowMagnifiableBounds(boolean show) { if (mRegistered) { mForceShowMagnifiableBounds = show; + if (traceEnabled()) { + logTrace("setForceShowMagnifiableBounds", + "displayID=" + mDisplayId + ";show=" + show); + } mControllerCtx.getWindowManager().setForceShowMagnifiableBounds( mDisplayId, show); } @@ -1255,6 +1274,16 @@ public class FullScreenMagnificationController { } } + private boolean traceEnabled() { + return mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MANAGER_INTERNAL); + } + + private void logTrace(String methodName, String params) { + mControllerCtx.getTraceManager().logTrace( + "WindowManagerInternal." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params); + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); @@ -1320,6 +1349,13 @@ public class FullScreenMagnificationController { mEnabled = enabled; if (!mEnabled) { mSentMagnificationSpec.clear(); + if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MANAGER_INTERNAL)) { + mControllerCtx.getTraceManager().logTrace( + "WindowManagerInternal.setMagnificationSpec", + FLAGS_WINDOW_MANAGER_INTERNAL, + "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec); + } mControllerCtx.getWindowManager().setMagnificationSpec( mDisplayId, mSentMagnificationSpec); } @@ -1367,6 +1403,13 @@ public class FullScreenMagnificationController { } mSentMagnificationSpec.setTo(spec); + if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MANAGER_INTERNAL)) { + mControllerCtx.getTraceManager().logTrace( + "WindowManagerInternal.setMagnificationSpec", + FLAGS_WINDOW_MANAGER_INTERNAL, + "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec); + } mControllerCtx.getWindowManager().setMagnificationSpec( mDisplayId, mSentMagnificationSpec); } @@ -1455,6 +1498,7 @@ public class FullScreenMagnificationController { public static class ControllerContext { private final Context mContext; private final AccessibilityManagerService mAms; + private final AccessibilityTraceManager mTrace; private final WindowManagerInternal mWindowManager; private final Handler mHandler; private final Long mAnimationDuration; @@ -1469,6 +1513,7 @@ public class FullScreenMagnificationController { long animationDuration) { mContext = context; mAms = ams; + mTrace = ams.getTraceManager(); mWindowManager = windowManager; mHandler = handler; mAnimationDuration = animationDuration; @@ -1491,6 +1536,14 @@ public class FullScreenMagnificationController { } /** + * @return AccessibilityTraceManager + */ + @NonNull + public AccessibilityTraceManager getTraceManager() { + return mTrace; + } + + /** * @return WindowManagerInternal */ @NonNull diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index f7d1b9a311ba..c3d8d4c2c96a 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -61,6 +61,7 @@ import android.view.ViewConfiguration; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.gestures.GestureUtils; /** @@ -142,12 +143,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH public FullScreenMagnificationGestureHandler(@UiContext Context context, FullScreenMagnificationController fullScreenMagnificationController, + AccessibilityTraceManager trace, Callback callback, boolean detectTripleTap, boolean detectShortcutTrigger, @NonNull WindowMagnificationPromptController promptController, int displayId) { - super(displayId, detectTripleTap, detectShortcutTrigger, callback); + super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback); if (DEBUG_ALL) { Log.i(mLogTag, "FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index f9aecd739d33..8aacafbd05c7 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -411,8 +411,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { if (mWindowMagnificationMgr == null) { mWindowMagnificationMgr = new WindowMagnificationManager(mContext, - mAms.getCurrentUserIdLocked(), - this); + mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager()); } return mWindowMagnificationMgr; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java index bbe40b6faf74..19b339645557 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java @@ -20,11 +20,13 @@ import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_UP; +import android.accessibilityservice.AccessibilityTrace; import android.annotation.NonNull; import android.util.Log; import android.util.Slog; import android.view.MotionEvent; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.BaseEventStreamTransformation; import java.util.ArrayDeque; @@ -99,14 +101,17 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo void onTripleTapped(int displayId, int mode); } + private final AccessibilityTraceManager mTrace; protected final Callback mCallback; protected MagnificationGestureHandler(int displayId, boolean detectTripleTap, boolean detectShortcutTrigger, + AccessibilityTraceManager trace, @NonNull Callback callback) { mDisplayId = displayId; mDetectTripleTap = detectTripleTap; mDetectShortcutTrigger = detectShortcutTrigger; + mTrace = trace; mCallback = callback; mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null; @@ -118,6 +123,12 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo if (DEBUG_ALL) { Slog.i(mLogTag, "onMotionEvent(" + event + ")"); } + if (mTrace.isA11yTracingEnabledForTypes( + AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) { + mTrace.logTrace("MagnificationGestureHandler.onMotionEvent", + AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE, + "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags); + } if (DEBUG_EVENT_STREAM) { storeEventInto(mDebugInputEventHistory, event); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java index 993027d1ca3c..527742536480 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java @@ -16,6 +16,9 @@ package com.android.server.accessibility.magnification; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK; import static android.os.IBinder.DeathRecipient; import android.annotation.NonNull; @@ -27,6 +30,8 @@ import android.view.accessibility.IWindowMagnificationConnection; import android.view.accessibility.IWindowMagnificationConnectionCallback; import android.view.accessibility.MagnificationAnimationCallback; +import com.android.server.accessibility.AccessibilityTraceManager; + /** * A wrapper of {@link IWindowMagnificationConnection}. */ @@ -36,9 +41,12 @@ class WindowMagnificationConnectionWrapper { private static final String TAG = "WindowMagnificationConnectionWrapper"; private final @NonNull IWindowMagnificationConnection mConnection; + private final @NonNull AccessibilityTraceManager mTrace; - WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) { + WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection, + @NonNull AccessibilityTraceManager trace) { mConnection = connection; + mTrace = trace; } //Should not use this instance anymore after calling it. @@ -52,9 +60,15 @@ class WindowMagnificationConnectionWrapper { boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback callback) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".enableWindowMagnification", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION, + "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX + + ";centerY=" + centerY + ";callback=" + callback); + } try { mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, - transformToRemoteCallback(callback)); + transformToRemoteCallback(callback, mTrace)); } catch (RemoteException e) { if (DBG) { Slog.e(TAG, "Error calling enableWindowMagnification()", e); @@ -65,6 +79,10 @@ class WindowMagnificationConnectionWrapper { } boolean setScale(int displayId, float scale) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION, + "displayId=" + displayId + ";scale=" + scale); + } try { mConnection.setScale(displayId, scale); } catch (RemoteException e) { @@ -78,8 +96,14 @@ class WindowMagnificationConnectionWrapper { boolean disableWindowMagnification(int displayId, @Nullable MagnificationAnimationCallback callback) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".disableWindowMagnification", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION, + "displayId=" + displayId + ";callback=" + callback); + } try { - mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback)); + mConnection.disableWindowMagnification(displayId, + transformToRemoteCallback(callback, mTrace)); } catch (RemoteException e) { if (DBG) { Slog.e(TAG, "Error calling disableWindowMagnification()", e); @@ -90,6 +114,10 @@ class WindowMagnificationConnectionWrapper { } boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_WINDOW_MAGNIFICATION_CONNECTION, + "displayId=" + displayId + ";offsetX=" + offsetX + ";offsetY=" + offsetY); + } try { mConnection.moveWindowMagnifier(displayId, offsetX, offsetY); } catch (RemoteException e) { @@ -102,6 +130,11 @@ class WindowMagnificationConnectionWrapper { } boolean showMagnificationButton(int displayId, int magnificationMode) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".showMagnificationButton", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION, + "displayId=" + displayId + ";mode=" + magnificationMode); + } try { mConnection.showMagnificationButton(displayId, magnificationMode); } catch (RemoteException e) { @@ -114,6 +147,10 @@ class WindowMagnificationConnectionWrapper { } boolean removeMagnificationButton(int displayId) { + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".removeMagnificationButton", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId); + } try { mConnection.removeMagnificationButton(displayId); } catch (RemoteException e) { @@ -126,6 +163,14 @@ class WindowMagnificationConnectionWrapper { } boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) { + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MAGNIFICATION_CONNECTION + | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { + mTrace.logTrace(TAG + ".setConnectionCallback", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION + | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + "callback=" + connectionCallback); + } try { mConnection.setConnectionCallback(connectionCallback); } catch (RemoteException e) { @@ -139,25 +184,38 @@ class WindowMagnificationConnectionWrapper { private static @Nullable IRemoteMagnificationAnimationCallback transformToRemoteCallback( - MagnificationAnimationCallback callback) { + MagnificationAnimationCallback callback, AccessibilityTraceManager trace) { if (callback == null) { return null; } - return new RemoteAnimationCallback(callback); + return new RemoteAnimationCallback(callback, trace); } private static class RemoteAnimationCallback extends IRemoteMagnificationAnimationCallback.Stub { - private final MagnificationAnimationCallback mCallback; + private final AccessibilityTraceManager mTrace; - RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) { + RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback, + @NonNull AccessibilityTraceManager trace) { mCallback = callback; + mTrace = trace; + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) { + mTrace.logTrace("RemoteAnimationCallback.constructor", + FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "callback=" + callback); + } } @Override public void onResult(boolean success) throws RemoteException { mCallback.onResult(success); + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) { + mTrace.logTrace("RemoteAnimationCallback.onResult", + FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "success=" + success); + } + } } } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java index 4fb9a03b8ac1..b26d36493a3e 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -34,6 +34,7 @@ import android.view.Display; import android.view.MotionEvent; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.gestures.MultiTap; import com.android.server.accessibility.gestures.MultiTapAndHold; @@ -89,9 +90,10 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl public WindowMagnificationGestureHandler(@UiContext Context context, WindowMagnificationManager windowMagnificationMgr, + AccessibilityTraceManager trace, Callback callback, boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) { - super(displayId, detectTripleTap, detectShortcutTrigger, callback); + super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback); if (DEBUG_ALL) { Slog.i(mLogTag, "WindowMagnificationGestureHandler() , displayId = " + displayId + ")"); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index 938cb73dac79..7a111d80b42e 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -16,6 +16,9 @@ package com.android.server.accessibility.magnification; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; @@ -39,6 +42,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.statusbar.StatusBarManagerInternal; /** @@ -111,11 +115,14 @@ public class WindowMagnificationManager implements } private final Callback mCallback; + private final AccessibilityTraceManager mTrace; - public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback) { + public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback, + AccessibilityTraceManager trace) { mContext = context; mUserId = userId; mCallback = callback; + mTrace = trace; } /** @@ -135,7 +142,7 @@ public class WindowMagnificationManager implements mConnectionWrapper = null; } if (connection != null) { - mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection); + mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace); } if (mConnectionWrapper != null) { @@ -197,7 +204,10 @@ public class WindowMagnificationManager implements } } } - + if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { + mTrace.logTrace(TAG + ".requestWindowMagnificationConnection", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect); + } final long identity = Binder.clearCallingIdentity(); try { final StatusBarManagerInternal service = LocalServices.getService( @@ -511,6 +521,12 @@ public class WindowMagnificationManager implements @Override public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) { + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { + mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + "displayId=" + displayId + ";bounds=" + bounds); + } synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { @@ -527,11 +543,23 @@ public class WindowMagnificationManager implements @Override public void onChangeMagnificationMode(int displayId, int magnificationMode) throws RemoteException { + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { + mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + "displayId=" + displayId + ";mode=" + magnificationMode); + } //TODO: Uses this method to change the magnification mode on non-default display. } @Override public void onSourceBoundsChanged(int displayId, Rect sourceBounds) { + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { + mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + "displayId=" + displayId + ";source=" + sourceBounds); + } synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { @@ -543,11 +571,23 @@ public class WindowMagnificationManager implements @Override public void onPerformScaleAction(int displayId, float scale) { + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { + mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + "displayId=" + displayId + ";scale=" + scale); + } mCallback.onPerformScaleAction(displayId, scale); } @Override public void onAccessibilityActionPerformed(int displayId) { + if (mTrace.isA11yTracingEnabledForTypes( + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { + mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed", + FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, + "displayId=" + displayId); + } mCallback.onAccessibilityActionPerformed(displayId); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 55b982b40611..cbc32385efa2 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -107,6 +107,7 @@ java_library_static { ":display-device-config", ":display-layout-config", ":device-state-config", + ":guiconstants_aidl", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/wm/EventLogTags.logtags", diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index b2d35f45ab44..ed7d185a5ef0 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -35,11 +35,14 @@ import android.hardware.CameraStreamStats; import android.hardware.ICameraService; import android.hardware.ICameraServiceProxy; import android.hardware.camera2.CameraMetadata; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.DeviceStateManager.FoldStateListener; import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.nfc.INfcAdapter; import android.os.Binder; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Message; import android.os.Process; @@ -57,8 +60,8 @@ import android.view.IDisplayWindowListener; import android.view.Surface; import android.view.WindowManagerGlobal; -import com.android.internal.annotations.GuardedBy; import com.android.framework.protobuf.nano.MessageNano; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -447,6 +450,8 @@ public class CameraServiceProxy extends SystemService } }; + private final FoldStateListener mFoldStateListener; + public CameraServiceProxy(Context context) { super(context); mContext = context; @@ -459,6 +464,14 @@ public class CameraServiceProxy extends SystemService // Don't keep any extra logging threads if not needed mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS); mLogWriterService.allowCoreThreadTimeOut(true); + + mFoldStateListener = new FoldStateListener(mContext, folded -> { + if (folded) { + setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); + } else { + clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); + } + }); } /** @@ -471,7 +484,7 @@ public class CameraServiceProxy extends SystemService * * @see #clearDeviceStateFlags(int) */ - public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) { + private void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) { synchronized (mLock) { mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE); mDeviceState |= deviceStateFlags; @@ -491,7 +504,7 @@ public class CameraServiceProxy extends SystemService * * @see #setDeviceStateFlags(int) */ - public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) { + private void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) { synchronized (mLock) { mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE); mDeviceState &= ~deviceStateFlags; @@ -555,6 +568,9 @@ public class CameraServiceProxy extends SystemService } catch (RemoteException e) { Log.e(TAG, "Failed to register display window listener!"); } + + mContext.getSystemService(DeviceStateManager.class) + .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener); } } diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 35f29579b417..9f806af5b444 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -43,6 +43,7 @@ abstract class DisplayDevice { // The display device does not manage these properties itself, they are set by // the display manager service. The display device shouldn't really be looking at these. private int mCurrentLayerStack = -1; + private int mCurrentFlags = 0; private int mCurrentOrientation = -1; private Rect mCurrentLayerStackRect; private Rect mCurrentDisplayRect; @@ -212,6 +213,19 @@ abstract class DisplayDevice { } /** + * Sets the display flags while in a transaction. + * + * Valid display flags: + * {@link SurfaceControl#DISPLAY_RECEIVES_INPUT} + */ + public final void setDisplayFlagsLocked(SurfaceControl.Transaction t, int flags) { + if (mCurrentFlags != flags) { + mCurrentFlags = flags; + t.setDisplayFlags(mDisplayToken, flags); + } + } + + /** * Sets the display projection while in a transaction. * * @param orientation defines the display's orientation @@ -298,6 +312,7 @@ abstract class DisplayDevice { pw.println("mUniqueId=" + mUniqueId); pw.println("mDisplayToken=" + mDisplayToken); pw.println("mCurrentLayerStack=" + mCurrentLayerStack); + pw.println("mCurrentFlags=" + mCurrentFlags); pw.println("mCurrentOrientation=" + mCurrentOrientation); pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect); pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 9acb4c8f471a..5186744d5c27 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -512,6 +514,11 @@ final class LogicalDisplay { boolean isBlanked) { // Set the layer stack. device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack); + // Also inform whether the device is the same one sent to inputflinger for its layerstack. + // TODO(b/188914255): Remove once input can dispatch against device vs layerstack. + device.setDisplayFlagsLocked(t, + device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE + ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0); // Set the color mode and allowed display mode. if (device == mPrimaryDisplayDevice) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index fd0f1c38938f..7955ecacdbf5 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2528,9 +2528,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); // Dispatch display id for InputMethodService to update context display. - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO( - MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken, - mMethodMap.get(mCurMethodId).getConfigChanges())); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_INITIALIZE_IME, + mMethodMap.get(mCurMethodId).getConfigChanges(), mCurMethod, mCurToken)); scheduleNotifyImeUidToAudioService(mCurMethodUid); if (mCurClient != null) { clearClientSessionLocked(mCurClient); @@ -4292,12 +4291,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub try { if (DEBUG) { Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: " - + msg.arg1); + + mCurTokenDisplayId); } final IBinder token = (IBinder) args.arg2; - ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1, - new InputMethodPrivilegedOperationsImpl(this, token), - (int) args.arg3); + ((IInputMethod) args.arg1).initializeInternal(token, + new InputMethodPrivilegedOperationsImpl(this, token), msg.arg1); } catch (RemoteException e) { } args.recycle(); diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java index f47aa487744a..0a69aec76306 100644 --- a/services/core/java/com/android/server/notification/VibratorHelper.java +++ b/services/core/java/com/android/server/notification/VibratorHelper.java @@ -39,6 +39,9 @@ public final class VibratorHelper { private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps + private static final int CHIRP_LEVEL_DURATION_MILLIS = 100; + private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100; + private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50; private final Vibrator mVibrator; private final long[] mDefaultPattern; @@ -102,6 +105,9 @@ public final class VibratorHelper { * @param insistent {@code true} if the vibration should loop until it is cancelled. */ public VibrationEffect createFallbackVibration(boolean insistent) { + if (mVibrator.hasFrequencyControl()) { + return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent); + } return createWaveformVibration(mFallbackPattern, insistent); } @@ -111,9 +117,32 @@ public final class VibratorHelper { * @param insistent {@code true} if the vibration should loop until it is cancelled. */ public VibrationEffect createDefaultVibration(boolean insistent) { + if (mVibrator.hasFrequencyControl()) { + return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent); + } return createWaveformVibration(mDefaultPattern, insistent); } + private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) { + VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform() + .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0) + .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration) + .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS) + .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration); + + if (insistent) { + return waveformBuilder + .addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS) + .build(/* repeat= */ 0); + } + + VibrationEffect singleBeat = waveformBuilder.build(); + return VibrationEffect.startComposition() + .addEffect(singleBeat) + .addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS) + .compose(); + } + private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) { int[] ar = resources.getIntArray(resId); if (ar == null) { diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index 3c9b1063ba93..04b5005aa283 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -16,10 +16,8 @@ package com.android.server.policy; -import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; -import android.hardware.ICameraService; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager.FoldStateListener; import android.hardware.display.DisplayManagerInternal; @@ -27,13 +25,11 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.util.Slog; import android.view.DisplayInfo; import android.view.IDisplayFoldListener; import com.android.server.DisplayThread; import com.android.server.LocalServices; -import com.android.server.camera.CameraServiceProxy; import com.android.server.wm.WindowManagerInternal; /** @@ -41,13 +37,8 @@ import com.android.server.wm.WindowManagerInternal; * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy. */ class DisplayFoldController { - private static final String TAG = "DisplayFoldController"; - private final WindowManagerInternal mWindowManagerInternal; private final DisplayManagerInternal mDisplayManagerInternal; - // Camera service proxy can be disabled through a config. - @Nullable - private final CameraServiceProxy mCameraServiceProxy; private final int mDisplayId; private final Handler mHandler; @@ -64,12 +55,10 @@ class DisplayFoldController { DisplayFoldController( Context context, WindowManagerInternal windowManagerInternal, - DisplayManagerInternal displayManagerInternal, - @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea, + DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea, Handler handler) { mWindowManagerInternal = windowManagerInternal; mDisplayManagerInternal = displayManagerInternal; - mCameraServiceProxy = cameraServiceProxy; mDisplayId = displayId; mFoldedArea = new Rect(foldedArea); mHandler = handler; @@ -124,16 +113,6 @@ class DisplayFoldController { } } - if (mCameraServiceProxy != null) { - if (folded) { - mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); - } else { - mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED); - } - } else { - Slog.w(TAG, "Camera service unavailable to toggle folded state."); - } - mDurationLogger.setDeviceFolded(folded); mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp); mFolded = folded; @@ -188,8 +167,6 @@ class DisplayFoldController { LocalServices.getService(WindowManagerInternal.class); final DisplayManagerInternal displayService = LocalServices.getService(DisplayManagerInternal.class); - final CameraServiceProxy cameraServiceProxy = - LocalServices.getService(CameraServiceProxy.class); final String configFoldedArea = context.getResources().getString( com.android.internal.R.string.config_foldedArea); @@ -201,6 +178,6 @@ class DisplayFoldController { } return new DisplayFoldController(context, windowManagerService, displayService, - cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler()); + displayId, foldedArea, DisplayThread.getHandler()); } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index a24319f7a98c..54d97eea1521 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CALLBACK; +import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK; import static android.os.Build.IS_USER; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; @@ -34,6 +36,7 @@ import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_P import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG; import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS; import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS; +import static com.android.server.accessibility.AccessibilityTraceProto.LOGGING_TYPE; import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME; import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME; import static com.android.server.accessibility.AccessibilityTraceProto.WHERE; @@ -42,6 +45,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.utils.RegionUtils.forEachRect; +import android.accessibilityservice.AccessibilityTrace; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; @@ -90,6 +94,7 @@ import android.view.animation.Interpolator; import com.android.internal.R; import com.android.internal.os.SomeArgs; import com.android.internal.util.TraceBuffer; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; @@ -99,8 +104,6 @@ import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallba import java.io.File; import java.io.IOException; import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -116,21 +119,16 @@ final class AccessibilityController { private static final String TAG = AccessibilityController.class.getSimpleName(); private static final Object STATIC_LOCK = new Object(); - static AccessibilityControllerInternal + static AccessibilityControllerInternalImpl getAccessibilityControllerInternal(WindowManagerService service) { return AccessibilityControllerInternalImpl.getInstance(service); } - private final AccessibilityTracing mAccessibilityTracing; + private final AccessibilityControllerInternalImpl mAccessibilityTracing; private final WindowManagerService mService; private static final Rect EMPTY_RECT = new Rect(); private static final float[] sTempFloats = new float[9]; - AccessibilityController(WindowManagerService service) { - mService = service; - mAccessibilityTracing = AccessibilityTracing.getInstance(service); - } - private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = new SparseArray<>(); @@ -138,10 +136,17 @@ final class AccessibilityController { // Set to true if initializing window population complete. private boolean mAllObserversInitialized = true; + AccessibilityController(WindowManagerService service) { + mService = service; + mAccessibilityTracing = + AccessibilityController.getAccessibilityControllerInternal(service); + } + boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace( TAG + ".setMagnificationCallbacks", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; callbacks={" + callbacks + "}"); } boolean result = false; @@ -172,25 +177,31 @@ final class AccessibilityController { /** * Sets a callback for observing which windows are touchable for the purposes - * of accessibility on specified display. + * of accessibility on specified display. When a display is reparented and becomes + * an embedded one, the {@link WindowsForAccessibilityCallback#onDisplayReparented(int)} + * will notify the accessibility framework to remove the un-used window observer of + * this embedded display. * * @param displayId The logical display id. * @param callback The callback. - * @return {@code false} if display id is not valid or an embedded display. + * @return {@code false} if display id is not valid or an embedded display when the callback + * isn't null. */ boolean setWindowsForAccessibilityCallback(int displayId, WindowsForAccessibilityCallback callback) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace( TAG + ".setWindowsForAccessibilityCallback", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId + "; callback={" + callback + "}"); } - final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); - if (dc == null) { - return false; - } if (callback != null) { + final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + return false; + } + WindowsForAccessibilityObserver observer = mWindowsForAccessibilityObserver.get(displayId); if (isEmbeddedDisplay(dc)) { @@ -209,21 +220,13 @@ final class AccessibilityController { if (Build.IS_DEBUGGABLE) { throw new IllegalStateException(errorMessage); } - removeObserverOfEmbeddedDisplay(observer); + removeObserversForEmbeddedChildDisplays(observer); mWindowsForAccessibilityObserver.remove(displayId); } observer = new WindowsForAccessibilityObserver(mService, displayId, callback); mWindowsForAccessibilityObserver.put(displayId, observer); mAllObserversInitialized &= observer.mInitialized; } else { - if (isEmbeddedDisplay(dc)) { - // If this display is an embedded one, its window observer should be removed along - // with the window observer of its parent display removed because the window - // observer of the embedded display and its parent display is the same, and would - // be removed together when stopping the window tracking of its parent display. So - // here don't need to do removing window observer of the embedded display again. - return true; - } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); if (windowsForA11yObserver == null) { @@ -234,16 +237,17 @@ final class AccessibilityController { throw new IllegalStateException(errorMessage); } } - removeObserverOfEmbeddedDisplay(windowsForA11yObserver); + removeObserversForEmbeddedChildDisplays(windowsForA11yObserver); mWindowsForAccessibilityObserver.remove(displayId); } return true; } void performComputeChangedWindowsNot(int displayId, boolean forceSend) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace( TAG + ".performComputeChangedWindowsNot", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId + "; forceSend=" + forceSend); } WindowsForAccessibilityObserver observer = null; @@ -260,8 +264,10 @@ final class AccessibilityController { } void setMagnificationSpec(int displayId, MagnificationSpec spec) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".setMagnificationSpec", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK + | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".setMagnificationSpec", + FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId + "; spec={" + spec + "}"); } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); @@ -276,8 +282,9 @@ final class AccessibilityController { } void getMagnificationRegion(int displayId, Region outMagnificationRegion) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".getMagnificationRegion", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".getMagnificationRegion", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion + "}"); } @@ -288,9 +295,10 @@ final class AccessibilityController { } void onRectangleOnScreenRequested(int displayId, Rect rectangle) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace( TAG + ".onRectangleOnScreenRequested", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; rectangle={" + rectangle + "}"); } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); @@ -301,9 +309,11 @@ final class AccessibilityController { } void onWindowLayersChanged(int displayId) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - TAG + ".onWindowLayersChanged", "displayId=" + displayId); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK + | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged", + FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, + "displayId=" + displayId); } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { @@ -316,15 +326,18 @@ final class AccessibilityController { } } - void onRotationChanged(DisplayContent displayContent) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".onRotationChanged", + void onDisplaySizeChanged(DisplayContent displayContent) { + + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK + | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onRotationChanged", + FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayContent={" + displayContent + "}"); } final int displayId = displayContent.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { - displayMagnifier.onRotationChanged(displayContent); + displayMagnifier.onDisplaySizeChanged(displayContent); } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); @@ -334,8 +347,9 @@ final class AccessibilityController { } void onAppWindowTransition(int displayId, int transition) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".onAppWindowTransition", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onAppWindowTransition", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; transition=" + transition); } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); @@ -346,8 +360,10 @@ final class AccessibilityController { } void onWindowTransition(WindowState windowState, int transition) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".onWindowTransition", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK + | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onWindowTransition", + FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "windowState={" + windowState + "}; transition=" + transition); } final int displayId = windowState.getDisplayId(); @@ -364,9 +380,9 @@ final class AccessibilityController { void onWindowFocusChangedNot(int displayId) { // Not relevant for the display magnifier. - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - TAG + ".onWindowFocusChangedNot", "displayId=" + displayId); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onWindowFocusChangedNot", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId); } WindowsForAccessibilityObserver observer = null; synchronized (mService.mGlobalLock) { @@ -426,12 +442,10 @@ final class AccessibilityController { } void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - TAG + ".onSomeWindowResizedOrMoved", - "displayIds={" + displayIds.toString() + "}", - "".getBytes(), - callingUid); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, + "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid); } // Not relevant for the display magnifier. for (int i = 0; i < displayIds.length; i++) { @@ -444,9 +458,10 @@ final class AccessibilityController { } void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace( TAG + ".drawMagnifiedRegionBorderIfNeeded", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; transaction={" + t + "}"); } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); @@ -457,8 +472,9 @@ final class AccessibilityController { } MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow", + FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}"); } final int displayId = windowState.getDisplayId(); @@ -470,17 +486,19 @@ final class AccessibilityController { } boolean hasCallbacks() { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".hasCallbacks"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK + | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".hasCallbacks", + FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK); } return (mDisplayMagnifiers.size() > 0 || mWindowsForAccessibilityObserver.size() > 0); } void setForceShowMagnifiableBounds(int displayId, boolean show) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds", - "displayId=" + displayId + "; show=" + show); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show); } final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); if (displayMagnifier != null) { @@ -497,39 +515,50 @@ final class AccessibilityController { void handleWindowObserverOfEmbeddedDisplay( int embeddedDisplayId, WindowState parentWindow, int callingUid) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".handleWindowObserverOfEmbeddedDisplay", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={" - + parentWindow + "}", - "".getBytes(), - callingUid); + + parentWindow + "}", "".getBytes(), callingUid); } if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) { return; } - // Finds the parent display of this embedded display - final int parentDisplayId; - WindowState candidate = parentWindow; - while (candidate != null) { - parentWindow = candidate; - candidate = parentWindow.getDisplayContent().getParentWindow(); + mService.mH.sendMessage(PooledLambda.obtainMessage( + AccessibilityController::updateWindowObserverOfEmbeddedDisplay, + this, embeddedDisplayId, parentWindow)); + } + + private void updateWindowObserverOfEmbeddedDisplay(int embeddedDisplayId, + WindowState parentWindow) { + final WindowsForAccessibilityObserver windowsForA11yObserver; + + synchronized (mService.mGlobalLock) { + // Finds the parent display of this embedded display + WindowState candidate = parentWindow; + while (candidate != null) { + parentWindow = candidate; + candidate = parentWindow.getDisplayContent().getParentWindow(); + } + final int parentDisplayId = parentWindow.getDisplayId(); + // Uses the observer of parent display + windowsForA11yObserver = mWindowsForAccessibilityObserver.get(parentDisplayId); } - parentDisplayId = parentWindow.getDisplayId(); - // Uses the observer of parent display - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(parentDisplayId); if (windowsForA11yObserver != null) { + windowsForA11yObserver.notifyDisplayReparented(embeddedDisplayId); windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId); - // Replaces the observer of embedded display to the one of parent display - mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver); + synchronized (mService.mGlobalLock) { + // Replaces the observer of embedded display to the one of parent display + mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver); + } } } void onImeSurfaceShownChanged(WindowState windowState, boolean shown) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged", - "windowState=" + windowState + "; shown=" + shown); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".onImeSurfaceShownChanged", + FLAGS_MAGNIFICATION_CALLBACK, "windowState=" + windowState + ";shown=" + shown); } final int displayId = windowState.getDisplayId(); final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); @@ -555,7 +584,7 @@ final class AccessibilityController { + "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver); } - private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver + private void removeObserversForEmbeddedChildDisplays(WindowsForAccessibilityObserver observerOfParentDisplay) { final IntArray embeddedDisplayIdList = observerOfParentDisplay.getAndClearEmbeddedDisplayIdList(); @@ -580,7 +609,7 @@ final class AccessibilityController { private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM; private static final boolean DEBUG_WINDOW_TRANSITIONS = false; - private static final boolean DEBUG_ROTATION = false; + private static final boolean DEBUG_DISPLAY_SIZE = false; private static final boolean DEBUG_LAYERS = false; private static final boolean DEBUG_RECTANGLE_REQUESTED = false; private static final boolean DEBUG_VIEWPORT_WINDOW = false; @@ -599,7 +628,7 @@ final class AccessibilityController { private final Handler mHandler; private final DisplayContent mDisplayContent; private final Display mDisplay; - private final AccessibilityTracing mAccessibilityTracing; + private final AccessibilityControllerInternalImpl mAccessibilityTracing; private final MagnificationCallbacks mCallbacks; @@ -618,11 +647,13 @@ final class AccessibilityController { mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); mMagnifedViewport = new MagnifiedViewport(); - mAccessibilityTracing = AccessibilityTracing.getInstance(mService); + mAccessibilityTracing = + AccessibilityController.getAccessibilityControllerInternal(mService); mLongAnimationDuration = mDisplayContext.getResources().getInteger( com.android.internal.R.integer.config_longAnimTime); - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor", + FLAGS_MAGNIFICATION_CALLBACK, "windowManagerService={" + windowManagerService + "}; displayContent={" + displayContent + "}; display={" + display + "}; callbacks={" + callbacks + "}"); @@ -630,9 +661,9 @@ final class AccessibilityController { } void setMagnificationSpec(MagnificationSpec spec) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".setMagnificationSpec", + FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}"); } mMagnifedViewport.updateMagnificationSpec(spec); mMagnifedViewport.recomputeBounds(); @@ -642,25 +673,26 @@ final class AccessibilityController { } void setForceShowMagnifiableBounds(boolean show) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds", + FLAGS_MAGNIFICATION_CALLBACK, "show=" + show); } mForceShowMagnifiableBounds = show; mMagnifedViewport.setMagnifiedRegionBorderShown(show, true); } boolean isForceShowingMagnifiableBounds() { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds", + FLAGS_MAGNIFICATION_CALLBACK); } return mForceShowMagnifiableBounds; } void onRectangleOnScreenRequested(Rect rectangle) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested", + FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}"); } if (DEBUG_RECTANGLE_REQUESTED) { Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); @@ -683,8 +715,9 @@ final class AccessibilityController { } void onWindowLayersChanged() { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace( + LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK); } if (DEBUG_LAYERS) { Slog.i(LOG_TAG, "Layers changed."); @@ -693,23 +726,24 @@ final class AccessibilityController { mService.scheduleAnimationLocked(); } - void onRotationChanged(DisplayContent displayContent) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}"); + void onDisplaySizeChanged(DisplayContent displayContent) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged", + FLAGS_MAGNIFICATION_CALLBACK, "displayContent={" + displayContent + "}"); } - if (DEBUG_ROTATION) { + if (DEBUG_DISPLAY_SIZE) { final int rotation = displayContent.getRotation(); Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) + " displayId: " + displayContent.getDisplayId()); } - mMagnifedViewport.onRotationChanged(); - mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); + mMagnifedViewport.onDisplaySizeChanged(); + mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED); } void onAppWindowTransition(int displayId, int transition) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".onAppWindowTransition", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; transition=" + transition); } if (DEBUG_WINDOW_TRANSITIONS) { @@ -733,8 +767,9 @@ final class AccessibilityController { } void onWindowTransition(WindowState windowState, int transition) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".onWindowTransition", + FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}; transition=" + transition); } if (DEBUG_WINDOW_TRANSITIONS) { @@ -791,18 +826,18 @@ final class AccessibilityController { } void onImeSurfaceShownChanged(boolean shown) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".onImeSurfaceShownChanged", + FLAGS_MAGNIFICATION_CALLBACK, "shown=" + shown); } mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED, shown ? 1 : 0, 0).sendToTarget(); } MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow", - "windowState={" + windowState + "}"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpecForWindow", + FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}"); } MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec(); if (spec != null && !spec.isNop()) { @@ -814,8 +849,9 @@ final class AccessibilityController { } void getMagnificationRegion(Region outMagnificationRegion) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion", + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion", + FLAGS_MAGNIFICATION_CALLBACK, "outMagnificationRegion={" + outMagnificationRegion + "}"); } // Make sure we're working with the most current bounds @@ -824,25 +860,26 @@ final class AccessibilityController { } void destroy() { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".destroy"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK); } mMagnifedViewport.destroyWindow(); } // Can be called outside of a surface transaction void showMagnificationBoundsIfNeeded() { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded", + FLAGS_MAGNIFICATION_CALLBACK); } mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) .sendToTarget(); } void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded", - "transition={" + t + "}"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded", + FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}"); } mMagnifedViewport.drawWindowIfNeeded(t); } @@ -887,7 +924,8 @@ final class AccessibilityController { if (mDisplayContext.getResources().getConfiguration().isScreenRound()) { mCircularPath = new Path(); - mDisplay.getRealSize(mScreenSize); + + getDisplaySizeLocked(mScreenSize); final int centerXY = mScreenSize.x / 2; mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW); } else { @@ -917,7 +955,7 @@ final class AccessibilityController { } void recomputeBounds() { - mDisplay.getRealSize(mScreenSize); + getDisplaySizeLocked(mScreenSize); final int screenWidth = mScreenSize.x; final int screenHeight = mScreenSize.y; @@ -1052,9 +1090,10 @@ final class AccessibilityController { || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; } - void onRotationChanged() { + void onDisplaySizeChanged() { // If we are showing the magnification border, hide it immediately so - // the user does not see strange artifacts during rotation. The screenshot + // the user does not see strange artifacts during display size changed caused by + // rotation or folding/unfolding the device. In the rotation case, the screenshot // used for rotation already has the border. After the rotation is complete // we will show the border. if (isMagnifying() || isForceShowingMagnifiableBounds()) { @@ -1112,6 +1151,12 @@ final class AccessibilityController { }, false /* traverseTopToBottom */ ); } + private void getDisplaySizeLocked(Point outSize) { + final Rect bounds = + mDisplayContent.getConfiguration().windowConfiguration.getBounds(); + outSize.set(bounds.width(), bounds.height()); + } + void dump(PrintWriter pw, String prefix) { mWindow.dump(pw, prefix); } @@ -1226,7 +1271,7 @@ final class AccessibilityController { void updateSize() { synchronized (mService.mGlobalLock) { - mDisplay.getRealSize(mScreenSize); + getDisplaySizeLocked(mScreenSize); mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888); invalidate(mDirtyRect); @@ -1365,7 +1410,7 @@ final class AccessibilityController { public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; - public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; + public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4; public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6; @@ -1397,9 +1442,8 @@ final class AccessibilityController { mCallbacks.onUserContextChanged(); } break; - case MESSAGE_NOTIFY_ROTATION_CHANGED: { - final int rotation = message.arg1; - mCallbacks.onRotationChanged(rotation); + case MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED: { + mCallbacks.onDisplaySizeChanged(); } break; case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { @@ -1482,7 +1526,7 @@ final class AccessibilityController { private final Handler mHandler; - private final AccessibilityTracing mAccessibilityTracing; + private final AccessibilityControllerInternalImpl mAccessibilityTracing; private final WindowsForAccessibilityCallback mCallback; @@ -1502,24 +1546,26 @@ final class AccessibilityController { mCallback = callback; mDisplayId = displayId; mHandler = new MyHandler(mService.mH.getLooper()); - mAccessibilityTracing = AccessibilityTracing.getInstance(mService); + mAccessibilityTracing = + AccessibilityController.getAccessibilityControllerInternal(mService); mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration .getSendRecurringAccessibilityEventsInterval(); computeChangedWindows(true); } void performComputeChangedWindows(boolean forceSend) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows", - "forceSend=" + forceSend); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".performComputeChangedWindows", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend); } mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); computeChangedWindows(forceSend); } void scheduleComputeChangedWindows() { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows"); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".scheduleComputeChangedWindows", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK); } if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, @@ -1542,6 +1588,13 @@ final class AccessibilityController { mEmbeddedDisplayIdList.add(displayId); } + void notifyDisplayReparented(int embeddedDisplayId) { + // Notifies the A11y framework the display is reparented and + // becomes an embedded display for removing the un-used + // displayWindowObserver of this embedded one. + mCallback.onDisplayReparented(embeddedDisplayId); + } + boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) { int wsLayer = mService.mPolicy.getWindowLayerLw(windowState); int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(), @@ -1594,9 +1647,9 @@ final class AccessibilityController { * @param forceSend Send the windows the accessibility even if they haven't changed. */ void computeChangedWindows(boolean forceSend) { - if (mAccessibilityTracing.isEnabled()) { - mAccessibilityTracing.logState( - LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".computeChangedWindows", + FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend); } if (DEBUG) { Slog.i(LOG_TAG, "computeChangedWindows()"); @@ -1945,8 +1998,8 @@ final class AccessibilityController { private static final class AccessibilityControllerInternalImpl implements AccessibilityControllerInternal { - private static AccessibilityControllerInternal sInstance; - static AccessibilityControllerInternal getInstance(WindowManagerService service) { + private static AccessibilityControllerInternalImpl sInstance; + static AccessibilityControllerInternalImpl getInstance(WindowManagerService service) { synchronized (STATIC_LOCK) { if (sInstance == null) { sInstance = new AccessibilityControllerInternalImpl(service); @@ -1956,18 +2009,23 @@ final class AccessibilityController { } private final AccessibilityTracing mTracing; + private volatile long mEnabledTracingFlags; + private AccessibilityControllerInternalImpl(WindowManagerService service) { mTracing = AccessibilityTracing.getInstance(service); + mEnabledTracingFlags = 0L; } @Override - public void startTrace() { + public void startTrace(long loggingTypes) { + mEnabledTracingFlags = loggingTypes; mTracing.startTrace(); } @Override public void stopTrace() { mTracing.stopTrace(); + mEnabledTracingFlags = 0L; } @Override @@ -1975,19 +2033,37 @@ final class AccessibilityController { return mTracing.isEnabled(); } + boolean isTracingEnabled(long flags) { + return (flags & mEnabledTracingFlags) != 0L; + } + + void logTrace(String where, long loggingTypes) { + logTrace(where, loggingTypes, ""); + } + + void logTrace(String where, long loggingTypes, String callingParams) { + logTrace(where, loggingTypes, callingParams, "".getBytes(), Binder.getCallingUid()); + } + + void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid) { + mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, + new HashSet<String>(Arrays.asList("logTrace"))); + } + @Override - public void logTrace( - String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] stackTrace) { - mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace); + public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) { + mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace, + ignoreStackEntries); } @Override - public void logTrace( - String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] callStack, long timeStamp, int processId, long threadId) { - mTracing.logState(where, callingParams, a11yDump, callingUid, callStack, timeStamp, - processId, threadId); + public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid, StackTraceElement[] callStack, long timeStamp, int processId, + long threadId, Set<String> ignoreStackEntries) { + mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack, + timeStamp, processId, threadId, ignoreStackEntries); } } @@ -2004,7 +2080,6 @@ final class AccessibilityController { private static final int BUFFER_CAPACITY = 1024 * 1024 * 12; private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb"; - private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/"; private static final String TAG = "AccessibilityTracing"; private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; @@ -2034,13 +2109,6 @@ final class AccessibilityController { return; } synchronized (mLock) { - try { - Files.createDirectories(Paths.get(TRACE_DIRECTORY)); - mTraceFile.createNewFile(); - } catch (Exception e) { - Slog.e(TAG, "Error: Failed to create trace file."); - return; - } mEnabled = true; mBuffer.resetBuffer(); } @@ -2071,106 +2139,150 @@ final class AccessibilityController { /** * Write an accessibility trace log entry. */ - void logState(String where) { + void logState(String where, long loggingTypes) { if (!mEnabled) { return; } - logState(where, ""); + logState(where, loggingTypes, ""); } /** * Write an accessibility trace log entry. */ - void logState(String where, String callingParams) { + void logState(String where, long loggingTypes, String callingParams) { if (!mEnabled) { return; } - logState(where, callingParams, "".getBytes()); + logState(where, loggingTypes, callingParams, "".getBytes()); } /** * Write an accessibility trace log entry. */ - void logState(String where, String callingParams, byte[] a11yDump) { + void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump) { if (!mEnabled) { return; } - logState(where, callingParams, a11yDump, Binder.getCallingUid()); + logState(where, loggingTypes, callingParams, a11yDump, Binder.getCallingUid(), + new HashSet<String>(Arrays.asList("logState"))); } /** * Write an accessibility trace log entry. */ - void logState( - String where, String callingParams, byte[] a11yDump, int callingUid) { + void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid, Set<String> ignoreStackEntries) { if (!mEnabled) { return; } StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); - - logState(where, callingParams, a11yDump, callingUid, stackTraceElements); + ignoreStackEntries.add("logState"); + logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTraceElements, + ignoreStackEntries); } /** * Write an accessibility trace log entry. */ - void logState(String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] stackTrace) { + void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) { if (!mEnabled) { return; } - - log(where, callingParams, a11yDump, callingUid, stackTrace, + log(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace, SystemClock.elapsedRealtimeNanos(), Process.myPid() + ":" + Application.getProcessName(), - Thread.currentThread().getId() + ":" + Thread.currentThread().getName()); + Thread.currentThread().getId() + ":" + Thread.currentThread().getName(), + ignoreStackEntries); } /** * Write an accessibility trace log entry. */ - void logState(String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] callingStack, long timeStamp, int processId, long threadId) { + void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid, StackTraceElement[] callingStack, long timeStamp, int processId, + long threadId, Set<String> ignoreStackEntries) { if (!mEnabled) { return; } - log(where, callingParams, a11yDump, callingUid, callingStack, timeStamp, - String.valueOf(processId), String.valueOf(threadId)); + log(where, loggingTypes, callingParams, a11yDump, callingUid, callingStack, timeStamp, + String.valueOf(processId), String.valueOf(threadId), ignoreStackEntries); } - private String toStackTraceString(StackTraceElement[] stackTraceElements) { + private String toStackTraceString(StackTraceElement[] stackTraceElements, + Set<String> ignoreStackEntries) { + if (stackTraceElements == null) { return ""; } + StringBuilder stringBuilder = new StringBuilder(); - boolean skip = true; - for (int i = 0; i < stackTraceElements.length; i++) { - if (stackTraceElements[i].toString().contains( - AccessibilityTracing.class.getSimpleName())) { - skip = false; - } else if (!skip) { - stringBuilder.append(stackTraceElements[i].toString()).append("\n"); + int i = 0; + + // Skip the first a few elements until after any ignoreStackEntries + int firstMatch = -1; + while (i < stackTraceElements.length) { + for (String ele : ignoreStackEntries) { + if (stackTraceElements[i].toString().contains(ele)) { + // found the first stack element containing the ignorable stack entries + firstMatch = i; + break; + } + } + if (firstMatch < 0) { + // Haven't found the first match yet, continue + i++; + } else { + break; + } + } + int lastMatch = firstMatch; + if (i < stackTraceElements.length) { + i++; + // Found the first match. Now look for the last match. + while (i < stackTraceElements.length) { + for (String ele : ignoreStackEntries) { + if (stackTraceElements[i].toString().contains(ele)) { + // This is a match. Look at the next stack element. + lastMatch = i; + break; + } + } + if (lastMatch != i) { + // Found a no-match. + break; + } + i++; } } + + i = lastMatch + 1; + while (i < stackTraceElements.length) { + stringBuilder.append(stackTraceElements[i].toString()).append("\n"); + i++; + } return stringBuilder.toString(); } /** * Write the current state to the buffer */ - private void log(String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] callingStack, long timeStamp, String processName, - String threadName) { + private void log(String where, long loggingTypes, String callingParams, byte[] a11yDump, + int callingUid, StackTraceElement[] callingStack, long timeStamp, + String processName, String threadName, Set<String> ignoreStackEntries) { SomeArgs args = SomeArgs.obtain(); args.arg1 = timeStamp; - args.arg2 = where; - args.arg3 = processName; - args.arg4 = threadName; - args.arg5 = callingUid; - args.arg6 = callingParams; - args.arg7 = callingStack; - args.arg8 = a11yDump; - mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget(); + args.arg2 = loggingTypes; + args.arg3 = where; + args.arg4 = processName; + args.arg5 = threadName; + args.arg6 = ignoreStackEntries; + args.arg7 = callingParams; + args.arg8 = callingStack; + args.arg9 = a11yDump; + + mHandler.obtainMessage( + LogHandler.MESSAGE_LOG_TRACE_ENTRY, callingUid, 0, args).sendToTarget(); } /** @@ -2199,8 +2311,6 @@ final class AccessibilityController { LocalServices.getService(PackageManagerInternal.class); long tokenOuter = os.start(ENTRY); - String callingStack = - toStackTraceString((StackTraceElement[]) args.arg7); long reportedTimeStampNanos = (long) args.arg1; long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); @@ -2213,13 +2323,25 @@ final class AccessibilityController { os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos); os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString()); - os.write(WHERE, (String) args.arg2); - os.write(PROCESS_NAME, (String) args.arg3); - os.write(THREAD_ID_NAME, (String) args.arg4); - os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg5)); - os.write(CALLING_PARAMS, (String) args.arg6); + + long loggingTypes = (long) args.arg2; + List<String> loggingTypeNames = + AccessibilityTrace.getNamesOfLoggingTypes(loggingTypes); + + for (String type : loggingTypeNames) { + os.write(LOGGING_TYPE, type); + } + os.write(WHERE, (String) args.arg3); + os.write(PROCESS_NAME, (String) args.arg4); + os.write(THREAD_ID_NAME, (String) args.arg5); + os.write(CALLING_PKG, pmInternal.getNameForUid(message.arg1)); + os.write(CALLING_PARAMS, (String) args.arg7); + + String callingStack = toStackTraceString( + (StackTraceElement[]) args.arg8, (Set<String>) args.arg6); + os.write(CALLING_STACKS, callingStack); - os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg8); + os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9); long tokenInner = os.start(WINDOW_MANAGER_SERVICE); synchronized (mService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 26f475ec8b29..a975ba6096ed 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -28,6 +28,11 @@ import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; +import static com.android.server.wm.ActivityRecord.State.DESTROYING; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; @@ -35,8 +40,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller; -import static com.android.server.wm.Task.ActivityState.DESTROYED; -import static com.android.server.wm.Task.ActivityState.DESTROYING; import android.annotation.NonNull; import android.app.Activity; @@ -68,6 +71,7 @@ import android.service.voice.VoiceInteractionManagerInternal; import android.util.Slog; import android.view.RemoteAnimationDefinition; import android.window.SizeConfigurationBuckets; +import android.window.TransitionInfo; import com.android.internal.app.AssistUtils; import com.android.internal.policy.IKeyguardDismissCallback; @@ -188,7 +192,7 @@ class ActivityClientController extends IActivityClientController.Stub { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped"); r = ActivityRecord.isInRootTaskLocked(token); if (r != null) { - if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) { + if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) { // The activity was requested to restart from // {@link #restartActivityProcessIfVisible}. restartingName = r.app.mName; @@ -1010,10 +1014,13 @@ class ActivityClientController extends IActivityClientController.Stub { final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { + if (r != null && r.isState(RESUMED, PAUSING)) { r.mDisplayContent.mAppTransition.overridePendingAppTransition( packageName, enterAnim, exitAnim, null, null, r.mOverrideTaskTransition); + mService.getTransitionController().setOverrideAnimation( + TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName, + enterAnim, exitAnim, r.mOverrideTaskTransition)); } } Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 0f6a71823334..b2e3fcbdccfb 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -775,7 +775,7 @@ class ActivityMetricsLogger { Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested + " state=" + r.getState() + " finishing=" + r.finishing); } - if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) { + if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) { // The activity may be launching while keyguard is locked. The keyguard may be dismissed // after the activity finished relayout, so skip the visibility check to avoid aborting // the tracking of launch event. diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d551f66c7387..5ec824d1612c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -129,6 +129,17 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; +import static com.android.server.wm.ActivityRecord.State.DESTROYING; +import static com.android.server.wm.ActivityRecord.State.FINISHING; +import static com.android.server.wm.ActivityRecord.State.INITIALIZING; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STARTED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN; import static com.android.server.wm.ActivityRecordProto.APP_STOPPED; import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE; @@ -191,18 +202,7 @@ import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; -import static com.android.server.wm.Task.ActivityState.DESTROYED; -import static com.android.server.wm.Task.ActivityState.DESTROYING; -import static com.android.server.wm.Task.ActivityState.FINISHING; -import static com.android.server.wm.Task.ActivityState.INITIALIZING; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.RESTARTING_PROCESS; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.ActivityState.STARTED; -import static com.android.server.wm.Task.ActivityState.STOPPED; -import static com.android.server.wm.Task.ActivityState.STOPPING; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; @@ -301,6 +301,7 @@ import android.view.InputApplicationHandle; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; +import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; @@ -314,12 +315,14 @@ import android.window.SplashScreen; import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.TaskSnapshot; +import android.window.TransitionInfo.AnimationOptions; import android.window.WindowContainerToken; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; +import com.android.internal.os.TransferPipe; import com.android.internal.policy.AttributeCache; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ToBooleanFunction; @@ -336,7 +339,6 @@ import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriPermissionOwner; import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot; import com.android.server.wm.SurfaceAnimator.AnimationType; -import com.android.server.wm.Task.ActivityState; import com.android.server.wm.WindowManagerService.H; import com.android.server.wm.utils.InsetUtils; @@ -345,6 +347,7 @@ import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -488,7 +491,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections. UriPermissionOwner uriPermissions; // current special URI access perms. WindowProcessController app; // if non-null, hosting application - private ActivityState mState; // current state we are in + private State mState; // current state we are in private Bundle mIcicle; // last saved activity state private PersistableBundle mPersistentState; // last persistently saved activity state private boolean mHaveState = true; // Indicates whether the last saved state of activity is @@ -550,6 +553,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A static final int LAUNCH_SOURCE_TYPE_HOME = 2; static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3; static final int LAUNCH_SOURCE_TYPE_APPLICATION = 4; + + enum State { + INITIALIZING, + STARTED, + RESUMED, + PAUSING, + PAUSED, + STOPPING, + STOPPED, + FINISHING, + DESTROYING, + DESTROYED, + RESTARTING_PROCESS + } + /** * The type of launch source. */ @@ -680,6 +698,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mInSizeCompatModeForBounds = false; // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio(). + // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration + // but that isn't reflected in this boolean. private boolean mIsAspectRatioApplied = false; // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed @@ -789,6 +809,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private final Configuration mTmpConfig = new Configuration(); private final Rect mTmpBounds = new Rect(); + private final Rect mTmpOutNonDecorBounds = new Rect(); // Token for targeting this activity for assist purposes. final Binder assistToken = new Binder(); @@ -1134,6 +1155,76 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLetterboxUiController.dump(pw, prefix); } + static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r, + String prefix, String label, boolean complete, boolean brief, boolean client, + String dumpPackage, boolean needNL, Runnable header, Task lastTask) { + if (dumpPackage != null && !dumpPackage.equals(r.packageName)) { + return false; + } + + final boolean full = !brief && (complete || !r.isInHistory()); + if (needNL) { + pw.println(""); + } + if (header != null) { + header.run(); + } + + String innerPrefix = prefix + " "; + String[] args = new String[0]; + if (lastTask != r.getTask()) { + lastTask = r.getTask(); + pw.print(prefix); + pw.print(full ? "* " : " "); + pw.println(lastTask); + if (full) { + lastTask.dump(pw, prefix + " "); + } else if (complete) { + // Complete + brief == give a summary. Isn't that obvious?!? + if (lastTask.intent != null) { + pw.print(prefix); + pw.print(" "); + pw.println(lastTask.intent.toInsecureString()); + } + } + } + pw.print(prefix); pw.print(full ? "* " : " "); pw.print(label); + pw.print(" #"); pw.print(index); pw.print(": "); + pw.println(r); + if (full) { + r.dump(pw, innerPrefix, true /* dumpAll */); + } else if (complete) { + // Complete + brief == give a summary. Isn't that obvious?!? + pw.print(innerPrefix); + pw.println(r.intent.toInsecureString()); + if (r.app != null) { + pw.print(innerPrefix); + pw.println(r.app); + } + } + if (client && r.attachedToProcess()) { + // flush anything that is already in the PrintWriter since the thread is going + // to write to the file descriptor directly + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + r.app.getThread().dumpActivity( + tp.getWriteFd(), r.appToken, innerPrefix, args); + // Short timeout, since blocking here can deadlock with the application. + tp.go(fd, 2000); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println(innerPrefix + "Failure while dumping the activity: " + e); + } catch (RemoteException e) { + pw.println(innerPrefix + "Got a RemoteException while dumping the activity"); + } + } + return true; + } + void setAppTimeTracker(AppTimeTracker att) { appTimeTracker = att; } @@ -1243,11 +1334,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A updatePictureInPictureMode(null, false); } else { mLastReportedMultiWindowMode = inMultiWindowMode; - computeConfigurationAfterMultiWindowModeChange(); // If the activity is in stopping or stopped state, for instance, it's in the // split screen task and not the top one, the last configuration it should keep // is the one before multi-window mode change. - final ActivityState state = getState(); + final State state = getState(); if (state != STOPPED && state != STOPPING) { ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS, true /* ignoreVisibility */); @@ -1270,31 +1360,25 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // precede the configuration change from the resize. mLastReportedPictureInPictureMode = inPictureInPictureMode; mLastReportedMultiWindowMode = inPictureInPictureMode; - if (targetRootTaskBounds != null && !targetRootTaskBounds.isEmpty()) { - computeConfigurationAfterMultiWindowModeChange(); - } ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS, true /* ignoreVisibility */); } } - private void computeConfigurationAfterMultiWindowModeChange() { - final Configuration newConfig = new Configuration(); - newConfig.setTo(task.getRequestedOverrideConfiguration()); - Rect outBounds = newConfig.windowConfiguration.getBounds(); - final Configuration parentConfig = task.getParent().getConfiguration(); - task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig); - task.computeConfigResourceOverrides(newConfig, parentConfig); - } - Task getTask() { return task; } + @Nullable + TaskFragment getTaskFragment() { + WindowContainer parent = getParent(); + return parent != null ? parent.asTaskFragment() : null; + } + @Override void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { - final Task oldTask = oldParent != null ? (Task) oldParent : null; - final Task newTask = newParent != null ? (Task) newParent : null; + final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null; + final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null; this.task = newTask; super.onParentChanged(newParent, oldParent); @@ -1352,11 +1436,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A updateColorTransform(); - if (oldTask != null) { - oldTask.cleanUpActivityReferences(this); + if (oldParent != null) { + ((TaskFragment) oldParent).cleanUpActivityReferences(this); } - if (newTask != null && isState(RESUMED)) { - newTask.setResumedActivity(this, "onParentChanged"); + + if (newParent != null && isState(RESUMED)) { + ((TaskFragment) newParent).setResumedActivity(this, "onParentChanged"); } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -2309,23 +2394,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * Reparents this activity into {@param newTask} at the provided {@param position}. The caller - * should ensure that the {@param newTask} is not already the parent of this activity. + * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The + * caller should ensure that the {@param newTaskFrag} is not already the parent of this + * activity. */ - void reparent(Task newTask, int position, String reason) { + void reparent(TaskFragment newTaskFrag, int position, String reason) { if (getParent() == null) { Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken); return; } - final Task prevTask = task; - if (prevTask == newTask) { - throw new IllegalArgumentException(reason + ": task=" + newTask + final TaskFragment prevTaskFrag = getTaskFragment(); + if (prevTaskFrag == newTaskFrag) { + throw new IllegalArgumentException(reason + ": task fragment =" + newTaskFrag + " is already the parent of r=" + this); } ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s" - + " to task=%d at %d", this, task.mTaskId, position); - reparent(newTask, position); + + " to new task fragment in task=%d at %d", this, task.mTaskId, position); + reparent(newTaskFrag, position); } private boolean isHomeIntent(Intent intent) { @@ -2891,7 +2977,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final Task rootTask = getRootTask(); - final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getResumedActivity() == null) + final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null) && rootTask.isFocusedRootTaskOnDisplay() // Do not adjust focus task because the task will be reused to launch new activity. && !task.isClearingToReuseTask(); @@ -2970,12 +3056,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Tell window manager to prepare for this one to be removed. setVisibility(false); - if (task.getPausingActivity() == null) { + if (getTaskFragment().getPausingActivity() == null) { ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this); if (DEBUG_USER_LEAVING) { Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false"); } - task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, + getTaskFragment().startPausing(false /* userLeaving */, false /* uiSleeping */, null /* resuming */, "finish"); } @@ -3106,8 +3192,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Clear last paused activity to ensure top activity can be resumed during sleeping. if (isNextNotYetVisible && mDisplayContent.isSleeping() - && next == next.getRootTask().mLastPausedActivity) { - next.getRootTask().mLastPausedActivity = null; + && next == next.getTaskFragment().mLastPausedActivity) { + next.getTaskFragment().clearLastPausedActivity(); } if (isCurrentVisible) { @@ -3294,8 +3380,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (DEBUG_SWITCH) { final Task task = getTask(); Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState() - + " resumed=" + task.getResumedActivity() - + " pausing=" + task.getPausingActivity() + + " resumed=" + task.getTopResumedActivity() + + " pausing=" + task.getTopPausingActivity() + " for reason " + reason); } return destroyImmediately(reason); @@ -3374,7 +3460,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Note: Call before {@link #removeFromHistory(String)}. */ void cleanUp(boolean cleanServices, boolean setState) { - task.cleanUpActivityReferences(this); + getTaskFragment().cleanUpActivityReferences(this); clearLastParentBeforePip(); // Clean up the splash screen if it was still displayed. @@ -3482,7 +3568,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // failed more than twice. Skip activities that's already finishing cleanly by itself. remove = false; } else if ((!mHaveState && !stateNotNeeded - && !isState(ActivityState.RESTARTING_PROCESS)) || finishing) { + && !isState(State.RESTARTING_PROCESS)) || finishing) { // Don't currently have state for the activity, or it is finishing -- always remove it. remove = true; } else if (!mVisibleRequested && launchCount > 2 @@ -4206,6 +4292,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) { final int animationType = pendingOptions.getAnimationType(); final DisplayContent displayContent = getDisplayContent(); + AnimationOptions options = null; switch (animationType) { case ANIM_CUSTOM: displayContent.mAppTransition.overridePendingAppTransition( @@ -4215,11 +4302,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pendingOptions.getAnimationStartedListener(), pendingOptions.getAnimationFinishedListener(), pendingOptions.getOverrideTaskTransition()); + options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(), + pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(), + pendingOptions.getOverrideTaskTransition()); break; case ANIM_CLIP_REVEAL: displayContent.mAppTransition.overridePendingAppTransitionClipReveal( pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getWidth(), pendingOptions.getHeight()); + options = AnimationOptions.makeClipRevealAnimOptions( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), @@ -4231,6 +4324,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A displayContent.mAppTransition.overridePendingAppTransitionScaleUp( pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getWidth(), pendingOptions.getHeight()); + options = AnimationOptions.makeScaleUpAnimOptions( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); if (intent.getSourceBounds() == null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), @@ -4246,6 +4342,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pendingOptions.getStartX(), pendingOptions.getStartY(), pendingOptions.getAnimationStartedListener(), scaleUp); + options = AnimationOptions.makeThumnbnailAnimOptions(buffer, + pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp); if (intent.getSourceBounds() == null && buffer != null) { intent.setSourceBounds(new Rect(pendingOptions.getStartX(), pendingOptions.getStartY(), @@ -4285,6 +4383,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A case ANIM_OPEN_CROSS_PROFILE_APPS: displayContent.mAppTransition .overridePendingAppTransitionStartCrossProfileApps(); + options = AnimationOptions.makeCrossProfileAnimOptions(); break; case ANIM_NONE: case ANIM_UNDEFINED: @@ -4293,6 +4392,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); break; } + + if (options != null) { + mAtmService.getTransitionController().setOverrideAnimation(options); + } } void clearAllDrawn() { @@ -4425,6 +4528,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + boolean getDeferHidingClient() { + return mDeferHidingClient; + } + @Override boolean isVisible() { // If the activity isn't hidden then it is considered visible and there is no need to check @@ -4589,7 +4696,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // If in a transition, defer commits for activities that are going invisible - if (!visible && mAtmService.getTransitionController().inTransition()) { + if (!visible && mAtmService.getTransitionController().inTransition(this)) { return; } // If we are preparing an app transition, then delay changing @@ -4838,7 +4945,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mCurrentLaunchCanTurnScreenOn; } - void setState(ActivityState state, String reason) { + void setState(State state, String reason) { ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s", this, getState(), state, reason); @@ -4850,8 +4957,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mState = state; - if (task != null) { - task.onActivityStateChanged(this, state, reason); + if (getTaskFragment() != null) { + getTaskFragment().onActivityStateChanged(this, state, reason); } // The WindowManager interprets the app stopping signal as @@ -4911,44 +5018,42 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - ActivityState getState() { + State getState() { return mState; } /** * Returns {@code true} if the Activity is in the specified state. */ - boolean isState(ActivityState state) { + boolean isState(State state) { return state == mState; } /** * Returns {@code true} if the Activity is in one of the specified states. */ - boolean isState(ActivityState state1, ActivityState state2) { + boolean isState(State state1, State state2) { return state1 == mState || state2 == mState; } /** * Returns {@code true} if the Activity is in one of the specified states. */ - boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) { + boolean isState(State state1, State state2, State state3) { return state1 == mState || state2 == mState || state3 == mState; } /** * Returns {@code true} if the Activity is in one of the specified states. */ - boolean isState(ActivityState state1, ActivityState state2, ActivityState state3, - ActivityState state4) { + boolean isState(State state1, State state2, State state3, State state4) { return state1 == mState || state2 == mState || state3 == mState || state4 == mState; } /** * Returns {@code true} if the Activity is in one of the specified states. */ - boolean isState(ActivityState state1, ActivityState state2, ActivityState state3, - ActivityState state4, ActivityState state5) { + boolean isState(State state1, State state2, State state3, State state4, State state5) { return state1 == mState || state2 == mState || state3 == mState || state4 == mState || state5 == mState; } @@ -4956,8 +5061,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * Returns {@code true} if the Activity is in one of the specified states. */ - boolean isState(ActivityState state1, ActivityState state2, ActivityState state3, - ActivityState state4, ActivityState state5, ActivityState state6) { + boolean isState(State state1, State state2, State state3, State state4, State state5, + State state6) { return state1 == mState || state2 == mState || state3 == mState || state4 == mState || state5 == mState || state6 == mState; } @@ -5161,13 +5266,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A supportsEnterPipOnTaskSwitch = false; break; case RESUMED: - // If the app is capable of entering PIP, we should try pausing it now - // so it can PIP correctly. - if (deferHidingClient) { - task.startPausingLocked(false /* uiSleeping */, - null /* resuming */, "makeInvisible"); - break; - } case INITIALIZING: case PAUSING: case PAUSED: @@ -5264,7 +5362,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private boolean shouldBeResumed(ActivityRecord activeActivity) { return shouldMakeActive(activeActivity) && isFocusable() - && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE + && getTaskFragment().getVisibility(activeActivity) + == TASK_FRAGMENT_VISIBILITY_VISIBLE && canResumeByCompat(); } @@ -5318,7 +5417,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (!task.hasChild(this)) { throw new IllegalStateException("Activity not found in its task"); } - return task.topRunningActivity() == this; + return getTaskFragment().topRunningActivity() == this; } void handleAlreadyVisible() { @@ -5407,16 +5506,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_STATES, "Activity paused: token=%s, timeout=%b", appToken, timeout); - if (task != null) { + final TaskFragment taskFragment = getTaskFragment(); + if (taskFragment != null) { removePauseTimeout(); - final ActivityRecord pausingActivity = task.getPausingActivity(); + final ActivityRecord pausingActivity = taskFragment.getPausingActivity(); if (pausingActivity == this) { ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this, (timeout ? "(due to timeout)" : " (pause complete)")); mAtmService.deferWindowLayout(); try { - task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */); + taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */); } finally { mAtmService.continueWindowLayout(); } @@ -6079,9 +6179,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return this; } // Try to use the one which is closest to top. - ActivityRecord r = rootTask.getResumedActivity(); + ActivityRecord r = rootTask.getTopResumedActivity(); if (r == null) { - r = rootTask.getPausingActivity(); + r = rootTask.getTopPausingActivity(); } if (r != null) { return r; @@ -6159,7 +6259,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // This would be redundant. return false; } - if (isState(RESUMED) || getRootTask() == null || this == task.getPausingActivity() + if (isState(RESUMED) || getRootTask() == null + || this == getTaskFragment().getPausingActivity() || !mHaveState || !stopped) { // We're not ready for this kind of thing. return false; @@ -6686,8 +6787,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final Configuration displayConfig = mDisplayContent.getConfiguration(); return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked( - appRect, insets, thumbnailHeader, task, displayConfig.uiMode, - displayConfig.orientation); + appRect, insets, thumbnailHeader, task, displayConfig.orientation); } @Override @@ -7112,7 +7212,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If the activity has requested override bounds, the configuration needs to be // computed accordingly. if (!matchParentBounds()) { - task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); + getTaskFragment().computeConfigResourceOverrides(resolvedConfig, + newParentConfiguration); } // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds // are already calculated in resolveFixedOrientationConfiguration. @@ -7227,7 +7328,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Since bounds has changed, the configuration needs to be computed accordingly. - task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); + getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); } /** @@ -7243,8 +7344,53 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation - * change and the requested orientation is different from the parent. + * In some cases, applying insets to bounds changes the orientation. For example, if a + * close-to-square display rotates to portrait to respect a portrait orientation activity, after + * insets such as the status and nav bars are applied, the activity may actually have a + * landscape orientation. This method checks whether the orientations of the activity window + * with and without insets match or if the orientation with insets already matches the + * requested orientation. If not, it may be necessary to letterbox the window. + * @param parentBounds are the new parent bounds passed down to the activity and should be used + * to compute the stable bounds. + * @param outBounds will store the stable bounds, which are the bounds with insets applied. + * These should be used to compute letterboxed bounds if orientation is not + * respected when insets are applied. + */ + private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) { + if (mDisplayContent == null) { + return true; + } + // Only need to make changes if activity sets an orientation + final int requestedOrientation = getRequestedConfigurationOrientation(); + if (requestedOrientation == ORIENTATION_UNDEFINED) { + return true; + } + // Compute parent orientation from bounds + final int orientation = parentBounds.height() >= parentBounds.width() + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + // Compute orientation from stable parent bounds (= parent bounds with insets applied) + final Task task = getTask(); + task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */, + outBounds /* outStableBounds */, parentBounds /* bounds */, + mDisplayContent.getDisplayInfo()); + final int orientationWithInsets = outBounds.height() >= outBounds.width() + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + // If orientation does not match the orientation with insets applied, then a + // display rotation will not be enough to respect orientation. However, even if they do + // not match but the orientation with insets applied matches the requested orientation, then + // there is no need to modify the bounds because when insets are applied, the activity will + // have the desired orientation. + return orientation == orientationWithInsets + || orientationWithInsets == requestedOrientation; + } + + /** + * Computes bounds (letterbox or pillarbox) when either: + * 1. The parent doesn't handle the orientation change and the requested orientation is + * different from the parent (see {@link DisplayContent#setIgnoreOrientationRequest()}. + * 2. The parent handling the orientation is not enough. This occurs when the display rotation + * may not be enough to respect orientation requests (see {@link + * ActivityRecord#orientationRespectedWithInsets}). * * <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied * in this method. @@ -7252,9 +7398,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig, int windowingMode) { mLetterboxBoundsForFixedOrientationAndAspectRatio = null; - if (handlesOrientationChangeFromDescendant()) { + final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); + final Rect containerBounds = new Rect(parentBounds); + boolean orientationRespectedWithInsets = + orientationRespectedWithInsets(parentBounds, containerBounds); + if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) { // No need to letterbox because of fixed orientation. Display will handle - // fixed-orientation requests. + // fixed-orientation requests and a display rotation is enough to respect requested + // orientation with insets applied. return; } if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable()) { @@ -7274,7 +7425,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If the activity requires a different orientation (either by override or activityInfo), // make it fit the available bounds by scaling down its bounds. final int forcedOrientation = getRequestedConfigurationOrientation(); - if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) { + if (forcedOrientation == ORIENTATION_UNDEFINED + || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) { return; } @@ -7286,67 +7438,83 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); - final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds(); - final Rect containingBounds = new Rect(); - final Rect containingAppBounds = new Rect(); - // Need to shrink the containing bounds into a square because the parent orientation does - // not match the activity requested orientation. - if (forcedOrientation == ORIENTATION_LANDSCAPE) { - // Shrink height to match width. Position height within app bounds. - final int bottom = Math.min(parentAppBounds.top + parentBounds.width(), - parentAppBounds.bottom); - containingBounds.set(parentBounds.left, parentAppBounds.top, parentBounds.right, - bottom); - containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, - parentAppBounds.right, bottom); - } else { - // Shrink width to match height. Position width within app bounds. - final int right = Math.min(parentAppBounds.left + parentBounds.height(), - parentAppBounds.right); - containingBounds.set(parentAppBounds.left, parentBounds.top, right, - parentBounds.bottom); - containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, right, - parentAppBounds.bottom); - } - - Rect mTmpFullBounds = new Rect(resolvedBounds); - resolvedBounds.set(containingBounds); + // TODO(b/182268157) merge aspect ratio logic here and in + // {@link ActivityRecord#applyAspectRatio} + // if no aspect ratio constraints are provided, parent aspect ratio is used + float aspectRatio = computeAspectRatio(parentBounds); // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with // set-fixed-orientation-letterbox-aspect-ratio. final float letterboxAspectRatioOverride = mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); - final float desiredAspectRatio = - letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO - ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds); - // Apply aspect ratio to resolved bounds - mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds, - containingBounds, desiredAspectRatio, true); - - // Vertically center if orientation is landscape. Bounds will later be horizontally centered - // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation. + aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO + ? letterboxAspectRatioOverride : aspectRatio; + + // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in + // order to use the extra available space. + final float maxAspectRatio = info.getMaxAspectRatio(); + final float minAspectRatio = info.getMinAspectRatio(); + if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) { + aspectRatio = maxAspectRatio; + } else if (aspectRatio < minAspectRatio) { + aspectRatio = minAspectRatio; + } + + // Store the current bounds to be able to revert to size compat mode values below if needed. + final Rect prevResolvedBounds = new Rect(resolvedBounds); + + // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored + // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()}, + // to ensure that aspect ratio is respected after insets are applied. + int activityWidth; + int activityHeight; if (forcedOrientation == ORIENTATION_LANDSCAPE) { - final int offsetY = parentBounds.centerY() - resolvedBounds.centerY(); - resolvedBounds.offset(0, offsetY); + activityWidth = parentBounds.width(); + // Compute height from stable bounds width to ensure orientation respected after insets. + activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio); + // Landscape is defined as width > height. To ensure activity is landscape when aspect + // ratio is close to 1, reduce the height by one pixel. + if (activityWidth == activityHeight) { + activityHeight -= 1; + } + // Center vertically within stable bounds in landscape to ensure insets do not trim + // height. + final int top = containerBounds.centerY() - activityHeight / 2; + resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight); + } else { + activityHeight = parentBounds.height(); + // Compute width from stable bounds height to ensure orientation respected after insets. + activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio); + // Center horizontally in portrait. For now, align to left and allow + // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center + // horizontally. Exclude left insets from parent to ensure cutout does not trim width. + final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds(); + resolvedBounds.set(parentAppBounds.left, parentBounds.top, + parentAppBounds.left + activityWidth, parentBounds.bottom); } if (mCompatDisplayInsets != null) { mCompatDisplayInsets.getBoundsByRotation( mTmpBounds, newParentConfig.windowConfiguration.getRotation()); - if (resolvedBounds.width() != mTmpBounds.width() - || resolvedBounds.height() != mTmpBounds.height()) { + // Insets may differ between different rotations, for example in the case of a display + // cutout. To ensure consistent bounds across rotations, compare the activity dimensions + // minus insets from the rotation the compat bounds were computed in. + Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds, + mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]); + if (activityWidth != mTmpBounds.width() + || activityHeight != mTmpBounds.height()) { // The app shouldn't be resized, we only do fixed orientation letterboxing if the // compat bounds are also from the same fixed orientation letterbox. Otherwise, // clear the fixed orientation bounds to show app in size compat mode. - resolvedBounds.set(mTmpFullBounds); + resolvedBounds.set(prevResolvedBounds); return; } } // Calculate app bounds using fixed orientation bounds because they will be needed later // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}. - task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); + getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(), + newParentConfig); mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds); } @@ -7370,11 +7538,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). if (!mTmpBounds.isEmpty()) { resolvedBounds.set(mTmpBounds); + // Exclude the horizontal decor area. + resolvedBounds.left = parentAppBounds.left; } if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) { // Compute the configuration based on the resolved bounds. If aspect ratio doesn't // restrict, the bounds should be the requested override bounds. - task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, + getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, getFixedRotationTransformDisplayInfo()); } } @@ -7430,14 +7600,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); } + // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in + // the container app bounds. Otherwise the entire container bounds are available. + final boolean fillContainer = resolvedBounds.equals(containingBounds); + if (!fillContainer) { + // The horizontal position should not cover insets. + resolvedBounds.left = containingAppBounds.left; + } // Use resolvedBounds to compute other override configurations such as appBounds. The bounds // are calculated in compat container space. The actual position on screen will be applied // later, so the calculation is simpler that doesn't need to involve offset from parent. - task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, + getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, mCompatDisplayInsets); // Use current screen layout as source because the size of app is independent to parent. - resolvedConfig.screenLayout = Task.computeScreenLayoutOverride( + resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride( getConfiguration().screenLayout, resolvedConfig.screenWidthDp, resolvedConfig.screenHeightDp); @@ -7496,7 +7673,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition. // Above coordinates are in "@" space, now place "*" and "#" to screen space. - final boolean fillContainer = resolvedBounds.equals(containingBounds); final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; final int screenPosY = containerBounds.top; if (screenPosX != 0 || screenPosY != 0) { @@ -7581,6 +7757,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (getUid() == SYSTEM_UID) { return false; } + // Do not sandbox to activity window bounds if the feature is disabled. + if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) { + return false; + } // Never apply sandboxing to an app that should be explicitly excluded from the config. if (info != null && info.neverSandboxDisplayApis()) { return false; @@ -7733,12 +7913,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } - private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, - Rect containingBounds) { - return applyAspectRatio(outBounds, containingAppBounds, containingBounds, - 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */); - } - /** * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is * made to outBounds. @@ -7747,19 +7921,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, - Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) { + Rect containingBounds) { final float maxAspectRatio = info.getMaxAspectRatio(); final Task rootTask = getRootTask(); final float minAspectRatio = info.getMinAspectRatio(); if (task == null || rootTask == null - || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets() - && !fixedOrientationLetterboxed) - || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1) + || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()) + || (maxAspectRatio == 0 && minAspectRatio == 0) || isInVrUiMode(getConfiguration())) { - // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in - // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we - // are in VR mode. + // We don't enforce aspect ratio if the activity task is in multiwindow unless it + // is in size-compat mode. We also don't set it if we are in VR mode. return false; } @@ -7767,30 +7939,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int containingAppHeight = containingAppBounds.height(); final float containingRatio = computeAspectRatio(containingAppBounds); - if (desiredAspectRatio < 1) { - desiredAspectRatio = containingRatio; - } - - if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) { - desiredAspectRatio = maxAspectRatio; - } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) { - desiredAspectRatio = minAspectRatio; - } - int activityWidth = containingAppWidth; int activityHeight = containingAppHeight; - if (containingRatio > desiredAspectRatio) { + if (containingRatio > maxAspectRatio && maxAspectRatio != 0) { if (containingAppWidth < containingAppHeight) { // Width is the shorter side, so we use that to figure-out what the max. height // should be given the aspect ratio. - activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f); + activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f); } else { // Height is the shorter side, so we use that to figure-out what the max. width // should be given the aspect ratio. - activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f); + activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f); } - } else if (containingRatio < desiredAspectRatio) { + } else if (containingRatio < minAspectRatio) { boolean adjustWidth; switch (getRequestedConfigurationOrientation()) { case ORIENTATION_LANDSCAPE: @@ -7818,9 +7980,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A break; } if (adjustWidth) { - activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f); + activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f); } else { - activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f); + activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f); } } @@ -7844,13 +8006,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } outBounds.set(containingBounds.left, containingBounds.top, right, bottom); - // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the - // container app bounds. Otherwise the entire container bounds are available. - if (!outBounds.equals(containingBounds)) { - // The horizontal position should not cover insets. - outBounds.left = containingAppBounds.left; - } - return true; } @@ -8606,6 +8761,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * compatibility mode activity compute the configuration without relying on its current display. */ static class CompatDisplayInsets { + /** The original rotation the compat insets were computed in */ + final @Rotation int mOriginalRotation; /** The container width on rotation 0. */ private final int mWidth; /** The container height on rotation 0. */ @@ -8632,6 +8789,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Constructs the environment to simulate the bounds behavior of the given container. */ CompatDisplayInsets(DisplayContent display, ActivityRecord container, @Nullable Rect fixedOrientationBounds) { + mOriginalRotation = display.getRotation(); mIsFloating = container.getWindowConfiguration().tasksAreFloating(); if (mIsFloating) { final Rect containerBounds = container.getWindowConfiguration().getBounds(); @@ -8778,7 +8936,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A outAppBounds.offset(insets.left, insets.top); } else if (rotation != ROTATION_UNDEFINED) { // Ensure the app bounds won't overlap with insets. - Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]); + TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds, + mNonDecorInsets[rotation]); } } } diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java index 8540fa7cf347..30c7b232fcc8 100644 --- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java +++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java @@ -16,10 +16,10 @@ package com.android.server.wm; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.RESUMED; import android.util.ArraySet; import android.util.Slog; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index e2ef82b81a18..3dc4892c874c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -28,6 +28,7 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -58,6 +59,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -74,7 +76,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; -import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -1549,6 +1550,11 @@ class ActivityStarter { newTransition.setRemoteTransition(remoteTransition); } mService.getTransitionController().collect(r); + // TODO(b/188669821): Remove when navbar reparenting moves to shell + if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null + && r.getOptions().getTransientLaunch()) { + mService.getTransitionController().setIsLegacyRecents(); + } try { mService.deferWindowLayout(); Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner"); @@ -1580,7 +1586,8 @@ class ActivityStarter { statusBar.collapsePanels(); } } - if (result == START_SUCCESS || result == START_TASK_TO_FRONT) { + final boolean started = result == START_SUCCESS || result == START_TASK_TO_FRONT; + if (started) { // The activity is started new rather than just brought forward, so record // it as an existence change. mService.getTransitionController().collectExistenceChange(r); @@ -1588,7 +1595,7 @@ class ActivityStarter { if (newTransition != null) { mService.getTransitionController().requestStartTransition(newTransition, mTargetTask, remoteTransition); - } else { + } else if (started) { // Make the collecting transition wait until this request is ready. mService.getTransitionController().setReady(false); } @@ -1650,7 +1657,6 @@ class ActivityStarter { * * Note: This method should only be called from {@link #startActivityUnchecked}. */ - // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner @VisibleForTesting int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, @@ -1757,7 +1763,7 @@ class ActivityStarter { mStartActivity.logStartActivity( EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask()); - mTargetRootTask.mLastPausedActivity = null; + mStartActivity.getTaskFragment().clearLastPausedActivity(); mRootWindowContainer.startPowerModeLaunchIfNeeded( false /* forceSend */, mStartActivity); @@ -2034,7 +2040,7 @@ class ActivityStarter { } // For paranoia, make sure we have correctly resumed the top activity. - topRootTask.mLastPausedActivity = null; + top.getTaskFragment().clearLastPausedActivity(); if (mDoResume) { mRootWindowContainer.resumeFocusedTasksTopActivities(); } @@ -2130,7 +2136,7 @@ class ActivityStarter { task.moveActivityToFrontLocked(act); act.updateOptionsLocked(mOptions); deliverNewIntent(act, intentGrants); - mTargetRootTask.mLastPausedActivity = null; + act.getTaskFragment().clearLastPausedActivity(); } else { mAddingToTask = true; } @@ -2558,7 +2564,7 @@ class ActivityStarter { */ private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity) { mTargetRootTask = intentActivity.getRootTask(); - mTargetRootTask.mLastPausedActivity = null; + intentActivity.getTaskFragment().clearLastPausedActivity(); Task intentTask = intentActivity.getTask(); // If the target task is not in the front, then we need to bring it to the front... // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 680937b37eac..d45cd06acfcd 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1216,8 +1216,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. final Task topFocusedRootTask = getTopDisplayFocusedRootTask(); - if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null - && topFocusedRootTask.getResumedActivity().info.applicationInfo.uid + if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null + && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid == Binder.getCallingUid()) { mAppSwitchesAllowed = true; } @@ -3740,6 +3740,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void detachNavigationBarFromApp(@NonNull IBinder transition) { + mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, + "detachNavigationBarFromApp"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + getTransitionController().legacyDetachNavigationBarFromApp(transition); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + void dumpLastANRLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)"); if (mLastANRState == null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index d3d1c1ca6a2b..08bca353f910 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -48,6 +48,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE; @@ -72,8 +75,6 @@ import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_R import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.PAUSING; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; import static com.android.server.wm.Task.TAG_CLEANUP; @@ -136,7 +137,6 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; -import com.android.internal.os.TransferPipe; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledConsumer; @@ -146,7 +146,6 @@ import com.android.server.am.UserState; import com.android.server.wm.ActivityMetricsLogger.LaunchingState; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -1968,76 +1967,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list, String prefix, String label, boolean complete, boolean brief, boolean client, String dumpPackage, boolean needNL, Runnable header, Task lastTask) { - String innerPrefix = null; - String[] args = null; boolean printed = false; - for (int i=list.size()-1; i>=0; i--) { + for (int i = list.size() - 1; i >= 0; i--) { final ActivityRecord r = list.get(i); - if (dumpPackage != null && !dumpPackage.equals(r.packageName)) { - continue; - } - if (innerPrefix == null) { - innerPrefix = prefix + " "; - args = new String[0]; - } - printed = true; - final boolean full = !brief && (complete || !r.isInHistory()); - if (needNL) { - pw.println(""); - needNL = false; - } - if (header != null) { - header.run(); - header = null; - } - if (lastTask != r.getTask()) { - lastTask = r.getTask(); - pw.print(prefix); - pw.print(full ? "* " : " "); - pw.println(lastTask); - if (full) { - lastTask.dump(pw, prefix + " "); - } else if (complete) { - // Complete + brief == give a summary. Isn't that obvious?!? - if (lastTask.intent != null) { - pw.print(prefix); pw.print(" "); - pw.println(lastTask.intent.toInsecureString()); - } - } - } - pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label); - pw.print(" #"); pw.print(i); pw.print(": "); - pw.println(r); - if (full) { - r.dump(pw, innerPrefix, true /* dumpAll */); - } else if (complete) { - // Complete + brief == give a summary. Isn't that obvious?!? - pw.print(innerPrefix); pw.println(r.intent.toInsecureString()); - if (r.app != null) { - pw.print(innerPrefix); pw.println(r.app); - } - } - if (client && r.attachedToProcess()) { - // flush anything that is already in the PrintWriter since the thread is going - // to write to the file descriptor directly - pw.flush(); - try { - TransferPipe tp = new TransferPipe(); - try { - r.app.getThread().dumpActivity( - tp.getWriteFd(), r.appToken, innerPrefix, args); - // Short timeout, since blocking here can deadlock with the application. - tp.go(fd, 2000); - } finally { - tp.kill(); - } - } catch (IOException e) { - pw.println(innerPrefix + "Failure while dumping the activity: " + e); - } catch (RemoteException e) { - pw.println(innerPrefix + "Got a RemoteException while dumping the activity"); - } - needNL = true; - } + ActivityRecord.dumpActivity(fd, pw, i, r, prefix, label, complete, brief, + client, dumpPackage, needNL, header, lastTask); + lastTask = r.getTask(); + header = null; + needNL = client && r.attachedToProcess(); } return printed; } @@ -2064,7 +2001,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { void updateTopResumedActivityIfNeeded() { final ActivityRecord prevTopActivity = mTopResumedActivity; final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); - if (topRootTask == null || topRootTask.getResumedActivity() == prevTopActivity) { + if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) { if (mService.isSleepingLocked()) { // There won't be a next resumed activity. The top process should still be updated // according to the current top focused activity. @@ -2086,7 +2023,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } // Update the current top activity. - mTopResumedActivity = topRootTask.getResumedActivity(); + mTopResumedActivity = topRootTask.getTopResumedActivity(); scheduleTopResumedActivityStateIfNeeded(); mService.updateTopApp(mTopResumedActivity); @@ -2393,8 +2330,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { String processName = null; int uid = 0; synchronized (mService.mGlobalLock) { - if (r.attachedToProcess() - && r.isState(Task.ActivityState.RESTARTING_PROCESS)) { + if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) { processName = r.app.mName; uid = r.app.mUid; } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index c1b287ff8077..ac687dc064ce 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -78,10 +78,7 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation; import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation; -import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; -import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP; -import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; -import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP; +import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE; @@ -102,12 +99,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Picture; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.hardware.HardwareBuffer; import android.os.Binder; import android.os.Debug; @@ -132,7 +124,6 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; -import android.view.animation.ClipRectAnimation; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.view.animation.ScaleAnimation; @@ -641,24 +632,6 @@ public class AppTransition implements Dump { /** * Prepares the specified animation with a standard duration, interpolator, etc. */ - Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth, - int appHeight, long duration, Interpolator interpolator) { - if (a != null) { - if (duration > 0) { - a.setDuration(duration); - } - a.setFillAfter(true); - if (interpolator != null) { - a.setInterpolator(interpolator); - } - a.initialize(appWidth, appHeight, appWidth, appHeight); - } - return a; - } - - /** - * Prepares the specified animation with a standard duration, interpolator, etc. - */ Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) { // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer @@ -678,56 +651,16 @@ public class AppTransition implements Dump { } /** - * Return the current thumbnail transition state. - */ - int getThumbnailTransitionState(boolean enter) { - if (enter) { - if (mNextAppTransitionScaleUp) { - return THUMBNAIL_TRANSITION_ENTER_SCALE_UP; - } else { - return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; - } - } else { - if (mNextAppTransitionScaleUp) { - return THUMBNAIL_TRANSITION_EXIT_SCALE_UP; - } else { - return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; - } - } - } - - /** * Creates an overlay with a background color and a thumbnail for the cross profile apps * animation. */ HardwareBuffer createCrossProfileAppsThumbnail( @DrawableRes int thumbnailDrawableRes, Rect frame) { - final int width = frame.width(); - final int height = frame.height(); - - final Picture picture = new Picture(); - final Canvas canvas = picture.beginRecording(width, height); - canvas.drawColor(Color.argb(0.6f, 0, 0, 0)); - final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.cross_profile_apps_thumbnail_size); - final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes); - drawable.setBounds( - (width - thumbnailSize) / 2, - (height - thumbnailSize) / 2, - (width + thumbnailSize) / 2, - (height + thumbnailSize) / 2); - drawable.setTint(mContext.getColor(android.R.color.white)); - drawable.draw(canvas); - picture.endRecording(); - - return Bitmap.createBitmap(picture).getHardwareBuffer(); + return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame); } Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) { - final Animation animation = - mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation(); - return prepareThumbnailAnimationWithDuration(animation, appRect.width(), - appRect.height(), 0, null); + return mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(appRect); } /** @@ -735,115 +668,14 @@ public class AppTransition implements Dump { * when a thumbnail is specified with the pending animation override. */ Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets, - HardwareBuffer thumbnailHeader, WindowContainer container, int uiMode, - int orientation) { - Animation a; - final int thumbWidthI = thumbnailHeader.getWidth(); - final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; - final int thumbHeightI = thumbnailHeader.getHeight(); - final int appWidth = appRect.width(); - - float scaleW = appWidth / thumbWidth; - getNextAppTransitionStartRect(container, mTmpRect); - final float fromX; - float fromY; - final float toX; - float toY; - final float pivotX; - final float pivotY; - if (shouldScaleDownThumbnailTransition(uiMode, orientation)) { - fromX = mTmpRect.left; - fromY = mTmpRect.top; - - // For the curved translate animation to work, the pivot points needs to be at the - // same absolute position as the one from the real surface. - toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left; - toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top; - pivotX = mTmpRect.width() / 2; - pivotY = appRect.height() / 2 / scaleW; - if (mGridLayoutRecentsEnabled) { - // In the grid layout, the header is displayed above the thumbnail instead of - // overlapping it. - fromY -= thumbHeightI; - toY -= thumbHeightI * scaleW; - } - } else { - pivotX = 0; - pivotY = 0; - fromX = mTmpRect.left; - fromY = mTmpRect.top; - toX = appRect.left; - toY = appRect.top; - } - final long duration = getAspectScaleDuration(); - final Interpolator interpolator = getAspectScaleInterpolator(); - if (mNextAppTransitionScaleUp) { - // Animation up from the thumbnail to the full screen - Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY); - scale.setInterpolator(interpolator); - scale.setDuration(duration); - Animation alpha = new AlphaAnimation(1f, 0f); - alpha.setInterpolator(mThumbnailFadeOutInterpolator); - alpha.setDuration(duration); - Animation translate = createCurvedMotion(fromX, toX, fromY, toY); - translate.setInterpolator(interpolator); - translate.setDuration(duration); - - mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI); - mTmpToClipRect.set(appRect); - - // Containing frame is in screen space, but we need the clip rect in the - // app space. - mTmpToClipRect.offsetTo(0, 0); - mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW); - mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW); - - if (contentInsets != null) { - mTmpToClipRect.inset((int) (-contentInsets.left * scaleW), - (int) (-contentInsets.top * scaleW), - (int) (-contentInsets.right * scaleW), - (int) (-contentInsets.bottom * scaleW)); - } - - Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect); - clipAnim.setInterpolator(interpolator); - clipAnim.setDuration(duration); - - // This AnimationSet uses the Interpolators assigned above. - AnimationSet set = new AnimationSet(false); - set.addAnimation(scale); - if (!mGridLayoutRecentsEnabled) { - // In the grid layout, the header should be shown for the whole animation. - set.addAnimation(alpha); - } - set.addAnimation(translate); - set.addAnimation(clipAnim); - a = set; - } else { - // Animation down from the full screen to the thumbnail - Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY); - scale.setInterpolator(interpolator); - scale.setDuration(duration); - Animation alpha = new AlphaAnimation(0f, 1f); - alpha.setInterpolator(mThumbnailFadeInInterpolator); - alpha.setDuration(duration); - Animation translate = createCurvedMotion(toX, fromX, toY, fromY); - translate.setInterpolator(interpolator); - translate.setDuration(duration); - - // This AnimationSet uses the Interpolators assigned above. - AnimationSet set = new AnimationSet(false); - set.addAnimation(scale); - if (!mGridLayoutRecentsEnabled) { - // In the grid layout, the header should be shown for the whole animation. - set.addAnimation(alpha); - } - set.addAnimation(translate); - a = set; - - } - return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0, - null); + HardwareBuffer thumbnailHeader, WindowContainer container, int orientation) { + AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get( + container.hashCode()); + return mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(appRect, + contentInsets, thumbnailHeader, orientation, spec != null ? spec.rect : null, + mDefaultNextAppTransitionAnimationSpec != null + ? mDefaultNextAppTransitionAnimationSpec.rect : null, + mNextAppTransitionScaleUp); } private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) { @@ -1043,7 +875,7 @@ public class AppTransition implements Dump { + "transit=%s Callers=%s", a, appTransitionOldToString(transit), Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) { - a = mTransitionAnimation.createClipRevealAnimationLocked( + a = mTransitionAnimation.createClipRevealAnimationLockedCompat( transit, enter, frame, displayFrame, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); @@ -1052,7 +884,7 @@ public class AppTransition implements Dump { + "transit=%s Callers=%s", a, appTransitionOldToString(transit), Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) { - a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame, + a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, @@ -1064,8 +896,8 @@ public class AppTransition implements Dump { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container); - a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked( - getThumbnailTransitionState(enter), frame, transit, thumbnailHeader, + a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter, + mNextAppTransitionScaleUp, frame, transit, thumbnailHeader, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, @@ -1080,9 +912,9 @@ public class AppTransition implements Dump { (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get( container.hashCode()); - a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked( - getThumbnailTransitionState(enter), orientation, transit, frame, - insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null, + a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter, + mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets, + stableInsets, freeform, spec != null ? spec.rect : null, mDefaultNextAppTransitionAnimationSpec != null ? mDefaultNextAppTransitionAnimationSpec.rect : null); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 8fbe1775fd19..2eeabf2b17ef 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -34,6 +34,7 @@ import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_ import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION; import android.annotation.CallSuper; +import android.annotation.NonNull; import android.app.WindowConfiguration; import android.content.res.Configuration; import android.graphics.Point; @@ -111,6 +112,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * This method should be used for getting settings applied in each particular level of the * hierarchy. */ + @NonNull public Configuration getConfiguration() { return mFullConfiguration; } @@ -170,11 +172,13 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { } /** Returns requested override configuration applied to this configuration container. */ + @NonNull public Configuration getRequestedOverrideConfiguration() { return mRequestedOverrideConfiguration; } /** Returns the resolved override configuration. */ + @NonNull Configuration getResolvedOverrideConfiguration() { return mResolvedOverrideConfiguration; } @@ -203,6 +207,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * Get merged override configuration from the top of the hierarchy down to this particular * instance. This should be reported to client as override config. */ + @NonNull public Configuration getMergedOverrideConfiguration() { return mMergedOverrideConfiguration; } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index baa27e34d625..99f6fd4771b7 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -495,8 +495,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { DisplayAreaInfo getDisplayAreaInfo() { - DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(), + final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(), getDisplayContent().getDisplayId(), mFeatureId); + final RootDisplayArea root = getRootDisplayArea(); + info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId; info.configuration.setTo(getConfiguration()); return info; } diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 47d7c9d1279d..3d7ac6c1a3f8 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; +import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; import android.annotation.Nullable; import android.os.Bundle; @@ -135,12 +136,6 @@ import java.util.function.BiFunction; */ class DisplayAreaPolicyBuilder { - /** - * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the - * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)} - */ - static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id"; - @Nullable private HierarchyBuilder mRootHierarchyBuilder; private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7d9971cc8013..8da5f08386c0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -94,6 +94,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_A import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS; @@ -116,7 +117,6 @@ import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; -import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT; @@ -361,6 +361,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean mIsSizeForced = false; /** + * Overridden display size and metrics to activity window bounds. Set via + * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging. + * @see WindowManagerService#setSandboxDisplayApis(int, boolean) + */ + private boolean mSandboxDisplayApis = true; + + /** * Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity} * but can be set from Settings or via shell command "adb shell wm density". * @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int) @@ -1012,6 +1019,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mAppTransition = new AppTransition(mWmService.mContext, mWmService, this); mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier); + mAtmService.getTransitionController().registerLegacyListener( + mWmService.mActivityManagerAppTransitionNotifier); mAppTransition.registerListenerLocked(mFixedRotationTransitionListener); mAppTransitionController = new AppTransitionController(mWmService, this); mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this); @@ -1909,10 +1918,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } } - - if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onRotationChanged(this); - } } void configureDisplayPolicy() { @@ -5600,6 +5605,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED); } + @Override + void onResize() { + super.onResize(); + if (mWmService.mAccessibilityController != null) { + mWmService.mAccessibilityController.onDisplaySizeChanged(this); + } + } + /** * If the launching rotated activity ({@link #mFixedRotationLaunchingApp}) is null, it simply * applies the rotation to display. Otherwise because the activity has shown as rotated, the @@ -5840,6 +5853,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } + /** + * Sets if Display APIs should be sandboxed to the activity window bounds. + */ + void setSandboxDisplayApis(boolean sandboxDisplayApis) { + mSandboxDisplayApis = sandboxDisplayApis; + } + + /** + * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds, + * {@code false} otherwise. Default to true, unless set for debugging purposes. + */ + boolean sandboxDisplayApis() { + return mSandboxDisplayApis; + } + /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */ class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 97c19ab72918..e5ea69866d10 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; @@ -39,6 +40,7 @@ import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.ViewRootImpl.computeWindowBounds; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; @@ -313,6 +315,13 @@ public class DisplayPolicy { private WindowState mSystemUiControllingWindow; + // Candidate window to determine the color of navigation bar. + private WindowState mNavBarColorWindowCandidate; + + // The window to determine opacity and background of translucent navigation bar. The window + // needs to be opaque. + private WindowState mNavBarBackgroundWindow; + private int mLastDisableFlags; private int mLastAppearance; private int mLastFullscreenAppearance; @@ -333,6 +342,7 @@ public class DisplayPolicy { private static final Rect sTmpRect = new Rect(); private static final Rect sTmpNavFrame = new Rect(); private static final Rect sTmpStatusFrame = new Rect(); + private static final Rect sTmpDecorFrame = new Rect(); private static final Rect sTmpScreenDecorFrame = new Rect(); private static final Rect sTmpLastParentFrame = new Rect(); private static final Rect sTmpDisplayFrameBounds = new Rect(); @@ -344,8 +354,6 @@ public class DisplayPolicy { private boolean mTopIsFullscreen; private boolean mForceStatusBar; private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED; - private boolean mForcingShowNavBar; - private int mForcingShowNavBarLayer; private boolean mForceShowSystemBars; private boolean mShowingDream; @@ -430,8 +438,10 @@ public class DisplayPolicy { final int displayId = displayContent.getDisplayId(); - mBarContentFrames.put(TYPE_STATUS_BAR, new Rect()); - mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect()); + if (!INSETS_LAYOUT_GENERALIZATION) { + mBarContentFrames.put(TYPE_STATUS_BAR, new Rect()); + mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect()); + } final Resources r = mContext.getResources(); mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer); @@ -613,6 +623,8 @@ public class DisplayPolicy { } }; displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener); + mService.mAtmService.getTransitionController().registerLegacyListener( + mAppTransitionListener); mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper, mService.mVrModeEnabled); @@ -1079,7 +1091,9 @@ public class DisplayPolicy { mStatusBar = win; final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider = (displayFrames, windowState, rect) -> { - rect.bottom = rect.top + getStatusBarHeight(displayFrames); + if (!INSETS_LAYOUT_GENERALIZATION) { + rect.bottom = rect.top + getStatusBarHeight(displayFrames); + } }; mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider); mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider); @@ -1089,18 +1103,22 @@ public class DisplayPolicy { mNavigationBar = win; mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowState, inOutFrame) -> { - - // In Gesture Nav, navigation bar frame is larger than frame to - // calculate inset. - if (navigationBarPosition(displayFrames.mDisplayWidth, - displayFrames.mDisplayHeight, - displayFrames.mRotation) == NAV_BAR_BOTTOM - && !mNavButtonForcedVisible) { - sTmpRect.set(inOutFrame); - sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe); - inOutFrame.top = sTmpRect.bottom - - getNavigationBarHeight(displayFrames.mRotation, - mDisplayContent.getConfiguration().uiMode); + if (INSETS_LAYOUT_GENERALIZATION) { + inOutFrame.inset(windowState.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets); + } else { + // In Gesture Nav, navigation bar frame is larger than frame to + // calculate inset. + if (navigationBarPosition(displayFrames.mDisplayWidth, + displayFrames.mDisplayHeight, + displayFrames.mRotation) == NAV_BAR_BOTTOM + && !mNavButtonForcedVisible) { + sTmpRect.set(inOutFrame); + sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + inOutFrame.top = sTmpRect.bottom + - getNavigationBarHeight(displayFrames.mRotation, + mDisplayContent.getConfiguration().uiMode); + } } }, @@ -1161,7 +1179,14 @@ public class DisplayPolicy { mExtraNavBarAltPosition = getAltBarPosition(attrs); break; } - mDisplayContent.setInsetProvider(insetsType, win, null); + if (!INSETS_LAYOUT_GENERALIZATION) { + mDisplayContent.setInsetProvider(insetsType, win, null); + } else { + mDisplayContent.setInsetProvider(insetsType, win, (displayFrames, + windowState, inOutFrame) -> inOutFrame.inset( + windowState.getLayoutingAttrs(displayFrames.mRotation) + .providedInternalInsets)); + } } } break; @@ -1252,8 +1277,17 @@ public class DisplayPolicy { } private int getStatusBarHeight(DisplayFrames displayFrames) { - return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation], - displayFrames.mDisplayCutoutSafe.top); + int statusBarHeight; + if (INSETS_LAYOUT_GENERALIZATION) { + if (mStatusBar != null) { + statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height; + } else { + statusBarHeight = 0; + } + } else { + statusBarHeight = mStatusBarHeightForRotation[displayFrames.mRotation]; + } + return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top); } WindowState getStatusBar() { @@ -1388,7 +1422,7 @@ public class DisplayPolicy { /** * @return true if the system bars are forced to stay visible */ - public boolean areSystemBarsForcedShownLw(WindowState windowState) { + public boolean areSystemBarsForcedShownLw() { return mForceShowSystemBars; } @@ -1423,13 +1457,30 @@ public class DisplayPolicy { WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames, Consumer<Rect> layout) { win.setSimulatedWindowFrames(simulatedWindowFrames); + final int requestedHeight = win.mRequestedHeight; + final int requestedWidth = win.mRequestedWidth; + if (INSETS_LAYOUT_GENERALIZATION) { + // Without a full layout process, in order to layout the system bars correctly, we need + // to set the requested size and the initial display frames to the window. + WindowManager.LayoutParams params = win.getLayoutingAttrs(displayFrames.mRotation); + win.setRequestedSize(params.width, params.height); + sTmpDecorFrame.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight); + simulatedWindowFrames.setFrames(sTmpDecorFrame /* parentFrame */, + sTmpDecorFrame /* displayFrame */); + simulatedWindowFrames.mIsSimulatingDecorWindow = true; + } final Rect contentFrame = new Rect(); try { layout.accept(contentFrame); } finally { win.setSimulatedWindowFrames(null); + if (INSETS_LAYOUT_GENERALIZATION) { + win.setRequestedSize(requestedWidth, requestedHeight); + } + } + if (!INSETS_LAYOUT_GENERALIZATION) { + contentFrames.put(win.mAttrs.type, contentFrame); } - contentFrames.put(win.mAttrs.type, contentFrame); mDisplayContent.getInsetsStateController().computeSimulatedState( win, displayFrames, simulatedWindowFrames); } @@ -1440,15 +1491,31 @@ public class DisplayPolicy { * some temporal states, but doesn't change the window frames used to show on screen. */ void simulateLayoutDisplay(DisplayFrames displayFrames, SparseArray<Rect> barContentFrames) { - final WindowFrames simulatedWindowFrames = new WindowFrames(); if (mNavigationBar != null) { - simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames, - barContentFrames, contentFrame -> layoutNavigationBar(displayFrames, - contentFrame)); + final WindowFrames simulatedWindowFrames = new WindowFrames(); + if (INSETS_LAYOUT_GENERALIZATION) { + simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames, + barContentFrames, + contentFrame -> simulateLayoutForContentFrame(displayFrames, + mNavigationBar, contentFrame)); + } else { + simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames, + barContentFrames, contentFrame -> layoutNavigationBar(displayFrames, + contentFrame)); + } } if (mStatusBar != null) { - simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames, - barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame)); + final WindowFrames simulatedWindowFrames = new WindowFrames(); + if (INSETS_LAYOUT_GENERALIZATION) { + simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames, + barContentFrames, + contentFrame -> simulateLayoutForContentFrame(displayFrames, + mStatusBar, contentFrame)); + } else { + simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames, + barContentFrames, + contentFrame -> layoutStatusBar(displayFrames, contentFrame)); + } } } @@ -1467,7 +1534,7 @@ public class DisplayPolicy { windowFrames.setFrames(sTmpStatusFrame /* parentFrame */, sTmpStatusFrame /* displayFrame */); // Let the status bar determine its size. - mStatusBar.computeFrameAndUpdateSourceFrame(); + mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames); // For layout, the status bar is always at the top with our fixed height. int statusBarBottom = displayFrames.mUnrestricted.top @@ -1518,18 +1585,18 @@ public class DisplayPolicy { } else if (navBarPosition == NAV_BAR_RIGHT) { // Landscape screen; nav bar goes to the right. navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right) - - getNavigationBarWidth(rotation, uiMode); + - getNavigationBarWidth(rotation, uiMode, navBarPosition); } else if (navBarPosition == NAV_BAR_LEFT) { // Seascape screen; nav bar goes to the left. navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left) - + getNavigationBarWidth(rotation, uiMode); + + getNavigationBarWidth(rotation, uiMode, navBarPosition); } // Compute the final frame. final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames(); windowFrames.setFrames(navigationFrame /* parentFrame */, navigationFrame /* displayFrame */); - mNavigationBar.computeFrameAndUpdateSourceFrame(); + mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames); sTmpRect.set(windowFrames.mFrame); sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); contentFrame.set(sTmpRect); @@ -1538,6 +1605,16 @@ public class DisplayPolicy { return navBarPosition; } + private void simulateLayoutForContentFrame(DisplayFrames displayFrames, WindowState win, + Rect simulatedContentFrame) { + layoutWindowLw(win, null /* attached */, displayFrames); + final Rect contentFrame = sTmpRect; + contentFrame.set(win.getLayoutingWindowFrames().mFrame); + // Excluding the display cutout before set to the simulated content frame. + contentFrame.intersect(displayFrames.mDisplayCutoutSafe); + simulatedContentFrame.set(contentFrame); + } + private boolean canReceiveInput(WindowState win) { boolean notFocusable = (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; @@ -1559,16 +1636,16 @@ public class DisplayPolicy { * @param displayFrames The display frames. */ public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { - if (win == mNavigationBar) { + if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) { mNavigationBarPosition = layoutNavigationBar(displayFrames, mBarContentFrames.get(TYPE_NAVIGATION_BAR)); return; } - if ((win == mStatusBar && !canReceiveInput(win))) { + if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) { layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR)); return; } - final WindowManager.LayoutParams attrs = win.getAttrs(); + final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation); final int type = attrs.type; final int fl = attrs.flags; @@ -1576,7 +1653,7 @@ public class DisplayPolicy { final int sim = attrs.softInputMode; displayFrames = win.getDisplayFrames(displayFrames); - final WindowFrames windowFrames = win.getWindowFrames(); + final WindowFrames windowFrames = win.getLayoutingWindowFrames(); sTmpLastParentFrame.set(windowFrames.mParentFrame); final Rect pf = windowFrames.mParentFrame; @@ -1587,7 +1664,13 @@ public class DisplayPolicy { final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; final InsetsState state = win.getInsetsState(); - computeWindowBounds(attrs, state, win.mToken.getBounds(), df); + if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) { + // Override the bounds in window token has many side effects. Directly use the display + // frame set for the simulated layout for this case. + computeWindowBounds(attrs, state, df, df); + } else { + computeWindowBounds(attrs, state, win.mToken.getBounds(), df); + } if (attached == null) { pf.set(df); if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { @@ -1687,7 +1770,17 @@ public class DisplayPolicy { windowFrames.setContentChanged(true); } - win.computeFrameAndUpdateSourceFrame(); + win.computeFrameAndUpdateSourceFrame(displayFrames); + if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) { + if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { + // Make sure that the zone we're avoiding for the cutout is at least as tall as the + // status bar; otherwise fullscreen apps will end up cutting halfway into the status + // bar. + displayFrames.mDisplayCutoutSafe.top = Math.max( + displayFrames.mDisplayCutoutSafe.top, + windowFrames.mFrame.bottom); + } + } } WindowState getTopFullscreenOpaqueWindow() { @@ -1706,9 +1799,9 @@ public class DisplayPolicy { mTopFullscreenOpaqueOrDimmingWindowState = null; mTopDockedOpaqueWindowState = null; mTopDockedOpaqueOrDimmingWindowState = null; + mNavBarColorWindowCandidate = null; + mNavBarBackgroundWindow = null; mForceStatusBar = false; - mForcingShowNavBar = false; - mForcingShowNavBarLayer = -1; mAllowLockscreenWhenOn = false; mShowingDream = false; @@ -1728,11 +1821,6 @@ public class DisplayPolicy { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi); applyKeyguardPolicy(win, imeTarget); final int fl = attrs.flags; - if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi - && attrs.type == TYPE_INPUT_METHOD) { - mForcingShowNavBar = true; - mForcingShowNavBarLayer = win.getSurfaceLayer(); - } boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW && attrs.type < FIRST_SYSTEM_WINDOW; @@ -1803,11 +1891,34 @@ public class DisplayPolicy { } } - // Check if the freeform window overlaps with the navigation bar area. final WindowState navBarWin = hasNavigationBar() ? mNavigationBar : null; - if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode() - && isOverlappingWithNavBar(win, navBarWin)) { - mIsFreeformWindowOverlappingWithNavBar = true; + if (isOverlappingWithNavBar(win, navBarWin)) { + // Check if the freeform window overlaps with the navigation bar area. + if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode()) { + mIsFreeformWindowOverlappingWithNavBar = true; + } + // Cache app window that overlaps with the navigation bar area to determine opacity and + // appearance of the navigation bar. We only need to cache one window because there + // should be only one overlapping window if it's not in gesture navigation mode; if it's + // in gesture navigation mode, the navigation bar will be NAV_BAR_FORCE_TRANSPARENT and + // its appearance won't be decided by overlapping windows. + if (affectsSystemUi) { + if ((appWindow && attached == null && attrs.isFullscreen()) + || attrs.type == TYPE_VOICE_INTERACTION) { + if (mNavBarColorWindowCandidate == null) { + mNavBarColorWindowCandidate = win; + } + if (mNavBarBackgroundWindow == null) { + mNavBarBackgroundWindow = win; + } + } else if (win.isDimming()) { + // For dimming window with it's host bounds overlapping with navigation bar, it + // can be used to determine navigation bar's color but not opacity. + if (mNavBarColorWindowCandidate == null) { + mNavBarColorWindowCandidate = win; + } + } + } } // Also keep track of any windows that are dimming but not necessarily fullscreen in the @@ -2116,11 +2227,37 @@ public class DisplayPolicy { return mUiContext; } - private int getNavigationBarWidth(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarWidthForRotationInCarMode[rotation]; + private int getNavigationBarWidth(int rotation, int uiMode, int position) { + if (INSETS_LAYOUT_GENERALIZATION) { + if (mNavigationBar == null) { + return 0; + } + LayoutParams lp = mNavigationBar.mAttrs; + if (lp.paramsForRotation != null + && lp.paramsForRotation.length == 4 + && lp.paramsForRotation[rotation] != null) { + lp = lp.paramsForRotation[rotation]; + } + if (position == NAV_BAR_LEFT) { + if (lp.width > lp.providedInternalInsets.right) { + return lp.width - lp.providedInternalInsets.right; + } else { + return 0; + } + } else if (position == NAV_BAR_RIGHT) { + if (lp.width > lp.providedInternalInsets.left) { + return lp.width - lp.providedInternalInsets.left; + } else { + return 0; + } + } + return lp.width; } else { - return mNavigationBarWidthForRotationDefault[rotation]; + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarWidthForRotationInCarMode[rotation]; + } else { + return mNavigationBarWidthForRotationDefault[rotation]; + } } } @@ -2147,7 +2284,7 @@ public class DisplayPolicy { if (hasNavigationBar()) { final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { - width -= getNavigationBarWidth(rotation, uiMode); + width -= getNavigationBarWidth(rotation, uiMode, navBarPosition); } } if (displayCutout != null) { @@ -2157,10 +2294,21 @@ public class DisplayPolicy { } private int getNavigationBarHeight(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarHeightForRotationInCarMode[rotation]; + if (INSETS_LAYOUT_GENERALIZATION) { + if (mNavigationBar == null) { + return 0; + } + LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation); + if (lp.height < lp.providedInternalInsets.top) { + return 0; + } + return lp.height - lp.providedInternalInsets.top; } else { - return mNavigationBarHeightForRotationDefault[rotation]; + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarHeightForRotationInCarMode[rotation]; + } else { + return mNavigationBarHeightForRotationDefault[rotation]; + } } } @@ -2177,10 +2325,17 @@ public class DisplayPolicy { * @return navigation bar frame height */ private int getNavigationBarFrameHeight(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarHeightForRotationInCarMode[rotation]; + if (INSETS_LAYOUT_GENERALIZATION) { + if (mNavigationBar == null) { + return 0; + } + return mNavigationBar.mAttrs.height; } else { - return mNavigationBarFrameHeightForRotationDefault[rotation]; + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarHeightForRotationInCarMode[rotation]; + } else { + return mNavigationBarFrameHeightForRotationDefault[rotation]; + } } } @@ -2301,9 +2456,9 @@ public class DisplayPolicy { if (position == NAV_BAR_BOTTOM) { outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode); } else if (position == NAV_BAR_RIGHT) { - outInsets.right = getNavigationBarWidth(displayRotation, uiMode); + outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position); } else if (position == NAV_BAR_LEFT) { - outInsets.left = getNavigationBarWidth(displayRotation, uiMode); + outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position); } } @@ -2329,6 +2484,17 @@ public class DisplayPolicy { @NavigationBarPosition int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { + if (INSETS_LAYOUT_GENERALIZATION && mNavigationBar != null) { + final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity; + switch (gravity) { + case Gravity.LEFT: + return NAV_BAR_LEFT; + case Gravity.RIGHT: + return NAV_BAR_RIGHT; + default: + return NAV_BAR_BOTTOM; + } + } if (navigationBarCanMove() && displayWidth > displayHeight) { if (displayRotation == Surface.ROTATION_270) { return NAV_BAR_LEFT; @@ -2486,8 +2652,6 @@ public class DisplayPolicy { final WindowState win = winCandidate; mSystemUiControllingWindow = win; - mDisplayContent.getInsetsPolicy().updateBarControlTarget(win); - final boolean inSplitScreen = mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated(); if (inSplitScreen) { @@ -2505,15 +2669,12 @@ public class DisplayPolicy { mTopDockedOpaqueOrDimmingWindowState); final int disableFlags = win.getDisableFlags(); final int opaqueAppearance = updateSystemBarsLw(win, disableFlags); - final WindowState navColorWin = chooseNavigationColorWindowLw( - mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, + final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate, mDisplayContent.mInputMethodWindow, mNavigationBarPosition); final boolean isNavbarColorManagedByIme = navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; - final int appearance = updateLightNavigationBarLw( - win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState, - mTopFullscreenOpaqueOrDimmingWindowState, - mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance; + final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance, + navColorWin) | opaqueAppearance; final int behavior = win.mAttrs.insetsFlags.behavior; final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR) || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); @@ -2572,8 +2733,7 @@ public class DisplayPolicy { @VisibleForTesting @Nullable - static WindowState chooseNavigationColorWindowLw(WindowState opaque, - WindowState opaqueOrDimming, WindowState imeWindow, + static WindowState chooseNavigationColorWindowLw(WindowState candidate, WindowState imeWindow, @NavigationBarPosition int navBarPosition) { // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME // window can be navigation color window. @@ -2582,71 +2742,59 @@ public class DisplayPolicy { && navBarPosition == NAV_BAR_BOTTOM && (imeWindow.mAttrs.flags & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; - - if (opaque != null && opaqueOrDimming == opaque) { - // If the top fullscreen-or-dimming window is also the top fullscreen, respect it - // unless IME window is also eligible, since currently the IME window is always show - // above the opaque fullscreen app window, regardless of the IME target window. - // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed. - return imeWindowCanNavColorWindow ? imeWindow : opaque; - } - - if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) { - // No dimming window is involved. Determine the result only with the IME window. - return imeWindowCanNavColorWindow ? imeWindow : null; - } - if (!imeWindowCanNavColorWindow) { - // No IME window is involved. Determine the result only with opaqueOrDimming. - return opaqueOrDimming; + // No IME window is involved. Determine the result only with candidate window. + return candidate; } - // The IME window and the dimming window are competing. Check if the dimming window can be - // IME target or not. - if (LayoutParams.mayUseInputMethod(opaqueOrDimming.mAttrs.flags)) { - // The IME window is above the dimming window. - return imeWindow; - } else { - // The dimming window is above the IME window. - return opaqueOrDimming; + if (candidate != null && candidate.isDimming()) { + // The IME window and the dimming window are competing. Check if the dimming window can + // be IME target or not. + if (LayoutParams.mayUseInputMethod(candidate.mAttrs.flags)) { + // The IME window is above the dimming window. + return imeWindow; + } else { + // The dimming window is above the IME window. + return candidate; + } } + + return imeWindow; } @VisibleForTesting - int updateLightNavigationBarLw(int appearance, WindowState opaque, - WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) { - - if (navColorWin != null) { - if (navColorWin == imeWindow || navColorWin == opaque) { - // Respect the light flag. - appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; - appearance |= navColorWin.mAttrs.insetsFlags.appearance - & APPEARANCE_LIGHT_NAVIGATION_BARS; - } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) { - // Clear the light flag for dimming window. - appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; - } - } - if (!isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) { + int updateLightNavigationBarLw(int appearance, WindowState navColorWin) { + if (navColorWin == null || navColorWin.isDimming() + || !isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) { + // Clear the light flag while not allowed. appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; + return appearance; } + + // Respect the light flag of navigation color window. + appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; + appearance |= navColorWin.mAttrs.insetsFlags.appearance + & APPEARANCE_LIGHT_NAVIGATION_BARS; return appearance; } private int updateSystemBarsLw(WindowState win, int disableFlags) { - final boolean dockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final boolean resizing = mDisplayContent.getDockedDividerController().isResizing(); - - // We need to force system bars when the docked root task is visible, when the freeform - // root task is focused but also when we are resizing for the transitions when docked - // root task visibility changes. - mForceShowSystemBars = dockedRootTaskVisible || win.inFreeformWindowingMode() || resizing; + final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final boolean multiWindowTaskVisible = + defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) + || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW); + final boolean freeformRootTaskVisible = + defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM); + + // We need to force showing system bars when the multi-window or freeform root task is + // visible. + mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible; + mDisplayContent.getInsetsPolicy().updateBarControlTarget(win); int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS; - appearance = configureStatusBarOpacity(appearance); - appearance = configureNavBarOpacity(appearance, dockedRootTaskVisible, resizing); + appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible, + freeformRootTaskVisible); final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); final long now = SystemClock.uptimeMillis(); @@ -2692,7 +2840,24 @@ public class DisplayPolicy { private Rect getBarContentFrameForWindow(WindowState win, int windowType) { final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType); - return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType); + if (rotatedBarFrame != null) { + return rotatedBarFrame; + } + if (!INSETS_LAYOUT_GENERALIZATION) { + return mBarContentFrames.get(windowType); + } + // We only need a window specific information for the fixed rotation, use raw insets state + // for all other cases. + InsetsState insetsState = mDisplayContent.getInsetsStateController().getRawInsetsState(); + final Rect tmpRect = new Rect(); + if (windowType == TYPE_NAVIGATION_BAR) { + tmpRect.set(insetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).getFrame()); + } + if (windowType == TYPE_STATUS_BAR) { + tmpRect.set(insetsState.getSource(InsetsState.ITYPE_STATUS_BAR).getFrame()); + } + tmpRect.intersect(mDisplayContent.mDisplayFrames.mDisplayCutoutSafe); + return tmpRect; } /** @@ -2748,53 +2913,35 @@ public class DisplayPolicy { * @return the current visibility flags with the nav-bar opacity related flags toggled based * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}. */ - private int configureNavBarOpacity(int appearance, boolean dockedRootTaskVisible, - boolean isDockedDividerResizing) { - final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isRootTaskVisible(WINDOWING_MODE_FREEFORM); - final boolean fullscreenDrawsBackground = - drawsBarBackground(mTopFullscreenOpaqueWindowState); - final boolean dockedDrawsBackground = - drawsBarBackground(mTopDockedOpaqueWindowState); + private int configureNavBarOpacity(int appearance, boolean multiWindowTaskVisible, + boolean freeformRootTaskVisible) { + final boolean drawBackground = drawsBarBackground(mNavBarBackgroundWindow); if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) { - if (fullscreenDrawsBackground && dockedDrawsBackground) { + if (drawBackground) { appearance = clearNavBarOpaqueFlag(appearance); - } else if (dockedRootTaskVisible) { - appearance = setNavBarOpaqueFlag(appearance); } } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { - if (dockedRootTaskVisible || freeformRootTaskVisible || isDockedDividerResizing) { + if (multiWindowTaskVisible || freeformRootTaskVisible) { if (mIsFreeformWindowOverlappingWithNavBar) { appearance = clearNavBarOpaqueFlag(appearance); - } else { - appearance = setNavBarOpaqueFlag(appearance); } - } else if (fullscreenDrawsBackground) { + } else if (drawBackground) { appearance = clearNavBarOpaqueFlag(appearance); } } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) { - if (isDockedDividerResizing) { - appearance = setNavBarOpaqueFlag(appearance); - } else if (freeformRootTaskVisible) { + if (freeformRootTaskVisible) { appearance = clearNavBarOpaqueFlag(appearance); - } else { - appearance = setNavBarOpaqueFlag(appearance); } } - if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_NAVIGATION_BAR) - || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_NAVIGATION_BAR)) { + if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, TYPE_NAVIGATION_BAR)) { appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS; } return appearance; } - private int setNavBarOpaqueFlag(int appearance) { - return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS; - } - private int clearNavBarOpaqueFlag(int appearance) { return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS; } @@ -2966,10 +3113,13 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState="); pw.println(mTopFullscreenOpaqueOrDimmingWindowState); } - if (mForcingShowNavBar) { - pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar); - pw.print(prefix); pw.print("mForcingShowNavBarLayer="); - pw.println(mForcingShowNavBarLayer); + if (mNavBarColorWindowCandidate != null) { + pw.print(prefix); pw.print("mNavBarColorWindowCandidate="); + pw.println(mNavBarColorWindowCandidate); + } + if (mNavBarBackgroundWindow != null) { + pw.print(prefix); pw.print("mNavBarBackgroundWindow="); + pw.println(mNavBarBackgroundWindow); } pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen); pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); @@ -3051,13 +3201,17 @@ public class DisplayPolicy { } @VisibleForTesting - static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) { + static boolean isOverlappingWithNavBar(@NonNull WindowState targetWindow, + WindowState navBarWindow) { if (navBarWindow == null || !navBarWindow.isVisible() || targetWindow.mActivityRecord == null || !targetWindow.isVisible()) { return false; } - return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame()); + // When the window is dimming means it's requesting dim layer to its host container, so + // checking whether it's overlapping with navigation bar by its container's bounds. + return Rect.intersects(targetWindow.isDimming() + ? targetWindow.getBounds() : targetWindow.getFrame(), navBarWindow.getFrame()); } /** diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 316c20ba5c47..2f0d703bb200 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -24,22 +24,22 @@ import android.util.Slog; /** Helper class to ensure activities are in the right visible state for a container. */ class EnsureActivitiesVisibleHelper { - private final Task mTask; + private final TaskFragment mTaskFragment; private ActivityRecord mTop; private ActivityRecord mStarting; private boolean mAboveTop; private boolean mContainerShouldBeVisible; - private boolean mBehindFullscreenActivity; + private boolean mBehindFullyOccludedContainer; private int mConfigChanges; private boolean mPreserveWindows; private boolean mNotifyClients; - EnsureActivitiesVisibleHelper(Task container) { - mTask = container; + EnsureActivitiesVisibleHelper(TaskFragment container) { + mTaskFragment = container; } /** - * Update all attributes except {@link mTask} to use in subsequent calculations. + * Update all attributes except {@link mTaskFragment} to use in subsequent calculations. * * @param starting The activity that is being started * @param configChanges Parts of the configuration that changed for this activity for evaluating @@ -51,12 +51,12 @@ class EnsureActivitiesVisibleHelper { void reset(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { mStarting = starting; - mTop = mTask.topRunningActivity(); + mTop = mTaskFragment.topRunningActivity(); // If the top activity is not fullscreen, then we need to make sure any activities under it // are now visible. mAboveTop = mTop != null; - mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting); - mBehindFullscreenActivity = !mContainerShouldBeVisible; + mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting); + mBehindFullyOccludedContainer = !mContainerShouldBeVisible; mConfigChanges = configChanges; mPreserveWindows = preserveWindows; mNotifyClients = notifyClients; @@ -85,22 +85,35 @@ class EnsureActivitiesVisibleHelper { Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop + " configChanges=0x" + Integer.toHexString(configChanges)); } - if (mTop != null) { - mTask.checkTranslucentActivityWaiting(mTop); + if (mTop != null && mTaskFragment.asTask() != null) { + // TODO(14709632): Check if this needed to be implemented in TaskFragment. + mTaskFragment.asTask().checkTranslucentActivityWaiting(mTop); } // We should not resume activities that being launched behind because these // activities are actually behind other fullscreen activities, but still required // to be visible (such as performing Recents animation). final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind - && mTask.isTopActivityFocusable() - && (starting == null || !starting.isDescendantOf(mTask)); - - mTask.forAllActivities(a -> { - setActivityVisibilityState(a, starting, resumeTopActivity); - }); - if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) { - mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows(); + && mTaskFragment.isTopActivityFocusable() + && (starting == null || !starting.isDescendantOf(mTaskFragment)); + + for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) { + final WindowContainer child = mTaskFragment.mChildren.get(i); + if (child.asTaskFragment() != null) { + final TaskFragment childTaskFragment = child.asTaskFragment(); + childTaskFragment.updateActivityVisibilities(starting, configChanges, + preserveWindows, notifyClients); + mBehindFullyOccludedContainer = childTaskFragment.getBounds().equals( + mTaskFragment.getBounds()); + if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) { + mAboveTop = false; + } + } else if (child.asActivityRecord() != null) { + setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity); + } + } + if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) { + mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows(); } } @@ -112,7 +125,7 @@ class EnsureActivitiesVisibleHelper { } mAboveTop = false; - r.updateVisibilityIgnoringKeyguard(mBehindFullscreenActivity); + r.updateVisibilityIgnoringKeyguard(mBehindFullyOccludedContainer); final boolean reallyVisible = r.shouldBeVisibleUnchecked(); // Check whether activity should be visible without Keyguard influence @@ -122,11 +135,11 @@ class EnsureActivitiesVisibleHelper { if (DEBUG_VISIBILITY) { Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r + " containerVisible=" + mContainerShouldBeVisible - + " behindFullscreen=" + mBehindFullscreenActivity); + + " behindFullyOccluded=" + mBehindFullyOccludedContainer); } - mBehindFullscreenActivity = true; + mBehindFullyOccludedContainer = true; } else { - mBehindFullscreenActivity = false; + mBehindFullyOccludedContainer = false; } } @@ -173,24 +186,25 @@ class EnsureActivitiesVisibleHelper { Slog.v(TAG_VISIBILITY, "Make invisible? " + r + " finishing=" + r.finishing + " state=" + r.getState() + " containerShouldBeVisible=" + mContainerShouldBeVisible - + " behindFullscreenActivity=" + mBehindFullscreenActivity + + " behindFullyOccludedContainer=" + mBehindFullyOccludedContainer + " mLaunchTaskBehind=" + r.mLaunchTaskBehind); } r.makeInvisible(); } - if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) { + if (!mBehindFullyOccludedContainer && mTaskFragment.isActivityTypeHome() + && r.isRootOfTask()) { if (DEBUG_VISIBILITY) { - Slog.v(TAG_VISIBILITY, "Home task: at " + mTask + Slog.v(TAG_VISIBILITY, "Home task: at " + mTaskFragment + " containerShouldBeVisible=" + mContainerShouldBeVisible - + " behindFullscreenActivity=" + mBehindFullscreenActivity); + + " behindOccludedParentContainer=" + mBehindFullyOccludedContainer); } // No other task in the root home task should be visible behind the home activity. // Home activities is usually a translucent activity with the wallpaper behind // them. However, when they don't have the wallpaper behind them, we want to // show activities in the next application root task behind them vs. another // task in the root home task like recents. - mBehindFullscreenActivity = true; + mBehindFullyOccludedContainer = true; } } @@ -219,7 +233,8 @@ class EnsureActivitiesVisibleHelper { r.setVisibility(true); } if (r != starting) { - mTask.mTaskSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */); + mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume, + true /* checkConfig */); } } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index da47328691c0..4f6a693b8c3f 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -29,6 +29,7 @@ import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_D import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Trace; import android.util.proto.ProtoOutputStream; import android.view.InsetsSource; @@ -90,6 +91,16 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { onSourceChanged(); } + @Override + void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) { + if (target != null && target.getWindow() != null) { + // ime control target could be a different window. + // Refer WindowState#getImeControlTarget(). + target = target.getWindow().getImeControlTarget(); + } + super.updateControlForTarget(target, force); + } + private void onSourceChanged() { if (mLastSource.equals(mSource)) { return; diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index 747d3652e150..f3b9cdfd39e0 100644 --- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; +import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; @@ -420,7 +421,7 @@ public class ImmersiveModeConfirmation { } final Bundle options = new Bundle(); - options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId); + options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId); return options; } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index f2f192686ad5..6b126ca088a7 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -18,8 +18,6 @@ package com.android.server.wm; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.InsetsController.ANIMATION_TYPE_HIDE; import static android.view.InsetsController.ANIMATION_TYPE_SHOW; import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN; @@ -135,11 +133,8 @@ class InsetsPolicy { abortTransient(); } mFocusedWin = focusedWin; - boolean forceShowsSystemBarsForWindowingMode = forceShowsSystemBarsForWindowingMode(); - InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin, - forceShowsSystemBarsForWindowingMode); - InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin, - forceShowsSystemBarsForWindowingMode); + InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin); + InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin); mStateController.onBarControlTargetChanged(statusControlTarget, getFakeControlTarget(focusedWin, statusControlTarget), navControlTarget, @@ -304,8 +299,7 @@ class InsetsPolicy { return realControlTarget == mDummyControlTarget ? focused : null; } - private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin, - boolean forceShowsSystemBarsForWindowingMode) { + private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin) { if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) { return mDummyControlTarget; } @@ -319,10 +313,9 @@ class InsetsPolicy { focusedWin.mAttrs.packageName); return mDisplayContent.mRemoteInsetsControlTarget; } - if (forceShowsSystemBarsForWindowingMode) { - // Status bar is forcibly shown for the windowing mode which is a steady state. - // We don't want the client to control the status bar, and we will dispatch the real - // visibility of status bar to the client. + if (mPolicy.areSystemBarsForcedShownLw()) { + // Status bar is forcibly shown. We don't want the client to control the status bar, and + // we will dispatch the real visibility of status bar to the client. return null; } if (forceShowsStatusBarTransiently()) { @@ -350,8 +343,7 @@ class InsetsPolicy { && !win.inMultiWindowMode(); } - private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin, - boolean forceShowsSystemBarsForWindowingMode) { + private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) { final WindowState imeWin = mDisplayContent.mInputMethodWindow; if (imeWin != null && imeWin.isVisible()) { // Force showing navigation bar while IME is visible. @@ -369,10 +361,9 @@ class InsetsPolicy { focusedWin.mAttrs.packageName); return mDisplayContent.mRemoteInsetsControlTarget; } - if (forceShowsSystemBarsForWindowingMode) { - // Navigation bar is forcibly shown for the windowing mode which is a steady state. - // We don't want the client to control the navigation bar, and we will dispatch the real - // visibility of navigation bar to the client. + if (mPolicy.areSystemBarsForcedShownLw()) { + // Navigation bar is forcibly shown. We don't want the client to control the navigation + // bar, and we will dispatch the real visibility of navigation bar to the client. return null; } if (forceShowsNavigationBarTransiently()) { @@ -417,19 +408,6 @@ class InsetsPolicy { && (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0; } - private boolean forceShowsSystemBarsForWindowingMode() { - final boolean isDockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final boolean isFreeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isRootTaskVisible(WINDOWING_MODE_FREEFORM); - final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing(); - - // We need to force system bars when the docked root task is visible, when the freeform - // root task is visible but also when we are resizing for the transitions when docked - // root task visibility changes. - return isDockedRootTaskVisible || isFreeformRootTaskVisible || isResizing; - } - @VisibleForTesting void startAnimation(boolean show, Runnable callback) { int typesReady = 0; diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 7daebff2ccc2..cbd1314b104a 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -307,11 +307,6 @@ class InsetsSourceProvider { // to control the window for now. return; } - if (target != null && target.getWindow() != null) { - // ime control target could be a different window. - // Refer WindowState#getImeControlTarget(). - target = target.getWindow().getImeControlTarget(); - } if (mWin != null && mWin.getSurfaceControl() == null) { // if window doesn't have a surface, set it null and return. diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 7174e68b06f4..eb7087cbc722 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -21,7 +21,6 @@ import android.content.Context; import android.graphics.Color; import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -105,12 +104,20 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and * the framework implementation will be used to determine the aspect ratio. */ - @VisibleForTesting void setFixedOrientationLetterboxAspectRatio(float aspectRatio) { mFixedOrientationLetterboxAspectRatio = aspectRatio; } /** + * Resets the aspect ratio of letterbox for fixed orientation to {@link + * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}. + */ + void resetFixedOrientationLetterboxAspectRatio() { + mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); + } + + /** * Gets the aspect ratio of letterbox for fixed orientation. */ float getFixedOrientationLetterboxAspectRatio() { @@ -118,6 +125,25 @@ final class LetterboxConfiguration { } /** + * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0, + * both it and a value of {@link + * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and + * and corners of the activity won't be rounded. + */ + void setLetterboxActivityCornersRadius(int cornersRadius) { + mLetterboxActivityCornersRadius = cornersRadius; + } + + /** + * Resets corners raidus for activities presented in the letterbox mode to {@link + * com.android.internal.R.integer.config_letterboxActivityCornersRadius}. + */ + void resetLetterboxActivityCornersRadius() { + mLetterboxActivityCornersRadius = mContext.getResources().getInteger( + com.android.internal.R.integer.config_letterboxActivityCornersRadius); + } + + /** * Whether corners of letterboxed activities are rounded. */ boolean isLetterboxActivityCornersRounded() { @@ -140,6 +166,25 @@ final class LetterboxConfiguration { return mLetterboxBackgroundColor; } + + /** + * Sets color of letterbox background which is used when {@link + * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as + * fallback for other backfround types. + */ + void setLetterboxBackgroundColor(Color color) { + mLetterboxBackgroundColor = color; + } + + /** + * Resets color of letterbox background to {@link + * com.android.internal.R.color.config_letterboxBackgroundColor}. + */ + void resetLetterboxBackgroundColor() { + mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor( + com.android.internal.R.color.config_letterboxBackgroundColor)); + } + /** * Gets {@link LetterboxBackgroundType} specified in {@link * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command. @@ -149,6 +194,19 @@ final class LetterboxConfiguration { return mLetterboxBackgroundType; } + /** Sets letterbox background type. */ + void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) { + mLetterboxBackgroundType = backgroundType; + } + + /** + * Resets cletterbox background type to {@link + * com.android.internal.R.integer.config_letterboxBackgroundType}. + */ + void resetLetterboxBackgroundType() { + mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); + } + /** Returns a string representing the given {@link LetterboxBackgroundType}. */ static String letterboxBackgroundTypeToString( @LetterboxBackgroundType int backgroundType) { @@ -178,6 +236,27 @@ final class LetterboxConfiguration { } /** + * Overrides alpha of a black scrim shown over wallpaper for {@link + * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}. + * + * <p>If given value is < 0 or >= 1, both it and a value of {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored + * and 0.0 (transparent) is instead. + */ + void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) { + mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha; + } + + /** + * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}. + */ + void resetLetterboxBackgroundWallpaperDarkScrimAlpha() { + mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); + } + + /** * Gets alpha of a black scrim shown over wallpaper letterbox background. */ float getLetterboxBackgroundWallpaperDarkScrimAlpha() { @@ -185,6 +264,28 @@ final class LetterboxConfiguration { } /** + * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in + * {@link mLetterboxBackgroundType}. + * + * <p> If given value <= 0, both it and a value of {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored + * and 0 is used instead. + */ + void setLetterboxBackgroundWallpaperBlurRadius(int radius) { + mLetterboxBackgroundWallpaperBlurRadius = radius; + } + + /** + * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link + * mLetterboxBackgroundType} to {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. + */ + void resetLetterboxBackgroundWallpaperBlurRadius() { + mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); + } + + /** * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link * mLetterboxBackgroundType}. */ @@ -211,9 +312,17 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and * central position (0.5) is used. */ - @VisibleForTesting void setLetterboxHorizontalPositionMultiplier(float multiplier) { mLetterboxHorizontalPositionMultiplier = multiplier; } + /** + * Resets horizontal position of a center of the letterboxed app window to {@link + * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}. + */ + void resetLetterboxHorizontalPositionMultiplier() { + mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier); + } + } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index a10b5d6e8177..7cd91646af1a 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -25,6 +25,8 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -149,8 +151,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan // Invisible activity should be stopped. If the recents activity is alive and its doesn't // need to relaunch by current configuration, then it may be already in stopped state. - if (!targetActivity.isState(Task.ActivityState.STOPPING, - Task.ActivityState.STOPPED)) { + if (!targetActivity.isState(STOPPING, STOPPED)) { // Add to stopping instead of stop immediately. So the client has the chance to perform // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more // things (e.g. the measure can be done earlier). The actual stop will be performed when diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6242b9791464..c2b9796a3d94 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -37,10 +37,10 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -53,6 +53,11 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.ActivityRecord.State.FINISHING; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; @@ -70,14 +75,9 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_P import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT; import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER; import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER; -import static com.android.server.wm.Task.ActivityState.FINISHING; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.ActivityState.STOPPED; -import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; -import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; @@ -1865,7 +1865,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (focusedRootTask == null) { return null; } - final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity(); + final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity(); if (resumedActivity != null && resumedActivity.app != null) { return resumedActivity; } @@ -1887,11 +1887,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // foreground. WindowProcessController fgApp = getItemFromRootTasks(rootTask -> { if (isTopDisplayFocusedRootTask(rootTask)) { - final ActivityRecord resumedActivity = rootTask.getResumedActivity(); + final ActivityRecord resumedActivity = rootTask.getTopResumedActivity(); if (resumedActivity != null) { return resumedActivity.app; - } else if (rootTask.getPausingActivity() != null) { - return rootTask.getPausingActivity().app; + } else if (rootTask.getTopPausingActivity() != null) { + return rootTask.getTopPausingActivity().app; } } return null; @@ -1917,7 +1917,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return; } - if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) { + if (rootTask.getVisibility(null /* starting */) + == TASK_FRAGMENT_VISIBILITY_INVISIBLE) { return; } @@ -2182,7 +2183,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // display area, so reparent. rootTask.reparent(taskDisplayArea, true /* onTop */); } - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask); + mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask); // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating @@ -2382,7 +2383,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (displayShouldSleep) { rootTask.goToSleepIfPossible(false /* shuttingDown */); } else { - rootTask.awakeFromSleepingLocked(); + rootTask.forAllLeafTasksAndLeafTaskFragments( + taskFragment -> taskFragment.awakeFromSleeping(), + true /* traverseTopToBottom */); if (rootTask.isFocusedRootTaskOnDisplay() && !mTaskSupervisor.getKeyguardController() .isKeyguardOrAodShowing(display.mDisplayId)) { @@ -2744,8 +2747,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (DEBUG_SWITCH) { Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState() - + " resumed=" + r.getTask().getResumedActivity() + " pausing=" - + r.getTask().getPausingActivity() + " for reason " + + " resumed=" + r.getTask().getTopResumedActivity() + " pausing=" + + r.getTask().getTopPausingActivity() + " for reason " + mDestroyAllActivitiesReason); } @@ -3332,7 +3335,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (rootTask == null || !rootTask.hasActivity()) { continue; } - final ActivityRecord resumedActivity = rootTask.getResumedActivity(); + final ActivityRecord resumedActivity = rootTask.getTopResumedActivity(); if (resumedActivity == null || !resumedActivity.idle) { ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s " + "not idle", rootTask.getRootTaskId(), resumedActivity); @@ -3347,7 +3350,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean allResumedActivitiesVisible() { boolean[] foundResumed = {false}; final boolean foundInvisibleResumedActivity = forAllRootTasks(rootTask -> { - final ActivityRecord r = rootTask.getResumedActivity(); + final ActivityRecord r = rootTask.getTopResumedActivity(); if (r != null) { if (!r.nowVisible) { return true; @@ -3365,7 +3368,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean allPausedActivitiesComplete() { boolean[] pausing = {true}; final boolean hasActivityNotCompleted = forAllLeafTasks(task -> { - final ActivityRecord r = task.getPausingActivity(); + final ActivityRecord r = task.getTopPausingActivity(); if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) { ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: " + "r=%s state=%s", r, r.getState()); @@ -3540,6 +3543,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } + // TODO(b/191434136): handle this properly when we add multi-window support on secondary + // display. private void calculateDefaultMinimalSizeOfResizeableTasks() { final Resources res = mService.mContext.getResources(); final float minimalSize = res.getDimension( diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 136a5a16fa7d..a6bf520561ac 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -27,13 +27,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; -import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; import static android.app.WindowConfiguration.windowingModeToString; @@ -43,7 +40,6 @@ import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; -import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -53,9 +49,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; @@ -65,7 +58,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; -import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; @@ -83,21 +75,18 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMA import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN; +import static com.android.server.wm.ActivityRecord.State.INITIALIZING; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STARTED; import static com.android.server.wm.ActivityRecord.TRANSFER_SPLASH_SCREEN_COPYING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION; @@ -110,7 +99,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; -import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; @@ -121,21 +109,12 @@ import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.ActivityState.STARTED; -import static com.android.server.wm.Task.ActivityState.STOPPING; -import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; import static com.android.server.wm.TaskProto.AFFINITY; import static com.android.server.wm.TaskProto.BOUNDS; import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER; -import static com.android.server.wm.TaskProto.DISPLAY_ID; import static com.android.server.wm.TaskProto.FILLS_PARENT; import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY; import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS; -import static com.android.server.wm.TaskProto.MIN_HEIGHT; -import static com.android.server.wm.TaskProto.MIN_WIDTH; import static com.android.server.wm.TaskProto.ORIG_ACTIVITY; import static com.android.server.wm.TaskProto.REAL_ACTIVITY; import static com.android.server.wm.TaskProto.RESIZE_MODE; @@ -143,7 +122,7 @@ import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY; import static com.android.server.wm.TaskProto.ROOT_TASK_ID; import static com.android.server.wm.TaskProto.SURFACE_HEIGHT; import static com.android.server.wm.TaskProto.SURFACE_WIDTH; -import static com.android.server.wm.TaskProto.WINDOW_CONTAINER; +import static com.android.server.wm.TaskProto.TASK_FRAGMENT; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.TASK; @@ -168,14 +147,7 @@ import android.app.AppGlobals; import android.app.IActivityController; import android.app.PictureInPictureParams; import android.app.RemoteAction; -import android.app.ResultInfo; import android.app.TaskInfo; -import android.app.WindowConfiguration; -import android.app.servertransaction.ActivityResultItem; -import android.app.servertransaction.ClientTransaction; -import android.app.servertransaction.NewIntentItem; -import android.app.servertransaction.PauseActivityItem; -import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -244,23 +216,21 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -class Task extends WindowContainer<WindowContainer> { +/** + * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job. + * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task} + * can also be an entity that showing in the Recents Screen for a job that user interacted with. + * A {@link Task} can also contain other {@link Task}s. + */ +class Task extends TaskFragment { private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM; - static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; - private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; static final String TAG_TASKS = TAG + POSTFIX_TASKS; - private static final String TAG_APP = TAG + POSTFIX_APP; static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP; - private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE; - private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; - private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK; - private static final String TAG_STATES = TAG + POSTFIX_STATES; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION; private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING; @@ -303,10 +273,6 @@ class Task extends WindowContainer<WindowContainer> { private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets"; private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size"; - // Set to false to disable the preview that is shown while a new activity - // is being started. - private static final boolean SHOW_APP_STARTING_PREVIEW = true; - // How long to wait for all background Activities to redraw following a call to // convertToTranslucent(). private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000; @@ -315,7 +281,6 @@ class Task extends WindowContainer<WindowContainer> { // code. static final int PERSIST_TASK_VERSION = 1; - static final int INVALID_MIN_SIZE = -1; private float mShadowRadius = 0; /** @@ -335,36 +300,6 @@ class Task extends WindowContainer<WindowContainer> { // Do not move the root task as a part of reparenting static final int REPARENT_LEAVE_ROOT_TASK_IN_PLACE = 2; - @IntDef(prefix = {"TASK_VISIBILITY"}, value = { - TASK_VISIBILITY_VISIBLE, - TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, - TASK_VISIBILITY_INVISIBLE, - }) - @interface TaskVisibility {} - - /** Task is visible. No other tasks on top that fully or partially occlude it. */ - static final int TASK_VISIBILITY_VISIBLE = 0; - - /** Task is partially occluded by other translucent task(s) on top of it. */ - static final int TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1; - - /** Task is completely invisible. */ - static final int TASK_VISIBILITY_INVISIBLE = 2; - - enum ActivityState { - INITIALIZING, - STARTED, - RESUMED, - PAUSING, - PAUSED, - STOPPING, - STOPPED, - FINISHING, - DESTROYING, - DESTROYED, - RESTARTING_PROCESS - } - // The topmost Activity passed to convertToTranslucent(). When non-null it means we are // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the @@ -446,7 +381,6 @@ class Task extends WindowContainer<WindowContainer> { CharSequence lastDescription; // Last description captured for this item. - Task mAdjacentTask; // Task adjacent to this one. int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent. Task mPrevAffiliate; // previous task in affiliated chain. int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence. @@ -458,21 +392,12 @@ class Task extends WindowContainer<WindowContainer> { String mCallingPackage; String mCallingFeatureId; - private final Rect mTmpStableBounds = new Rect(); - private final Rect mTmpNonDecorBounds = new Rect(); - private final Rect mTmpBounds = new Rect(); - private final Rect mTmpInsets = new Rect(); - private final Rect mTmpFullBounds = new Rect(); private static final Rect sTmpBounds = new Rect(); // Last non-fullscreen bounds the task was launched in or resized to. // The information is persisted and used to determine the appropriate root task to launch the // task into on restore. Rect mLastNonFullscreenBounds = null; - // Minimal width and height of this task when it's resizeable. -1 means it should use the - // default minimal width/height. - int mMinWidth; - int mMinHeight; // The surface transition of the target when recents animation is finished. // This is originally introduced to carry out the current surface control position and window @@ -497,10 +422,6 @@ class Task extends WindowContainer<WindowContainer> { /** Used by fillTaskInfo */ final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport(); - final ActivityTaskManagerService mAtmService; - final ActivityTaskSupervisor mTaskSupervisor; - final RootWindowContainer mRootWindowContainer; - /* Unique identifier for this task. */ final int mTaskId; /* User for which this task was created. */ @@ -571,29 +492,6 @@ class Task extends WindowContainer<WindowContainer> { /** ActivityRecords that are exiting, but still on screen for animations. */ final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>(); - /** - * When we are in the process of pausing an activity, before starting the - * next one, this variable holds the activity that is currently being paused. - * - * Only set at leaf tasks. - */ - @Nullable - private ActivityRecord mPausingActivity = null; - - /** - * This is the last activity that we put into the paused state. This is - * used to determine if we need to do an activity transition while sleeping, - * when we normally hold the top activity paused. - */ - ActivityRecord mLastPausedActivity = null; - - /** - * Current activity that is resumed, or null if there is none. - * Only set at leaf tasks. - */ - @Nullable - private ActivityRecord mResumedActivity = null; - private boolean mForceShowForAllUsers; /** When set, will force the task to report as invisible. */ @@ -641,121 +539,6 @@ class Task extends WindowContainer<WindowContainer> { } private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper(); - private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper = - new EnsureActivitiesVisibleHelper(this); - private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper = - new EnsureVisibleActivitiesConfigHelper(); - private class EnsureVisibleActivitiesConfigHelper { - private boolean mUpdateConfig; - private boolean mPreserveWindow; - private boolean mBehindFullscreen; - - void reset(boolean preserveWindow) { - mPreserveWindow = preserveWindow; - mUpdateConfig = false; - mBehindFullscreen = false; - } - - void process(ActivityRecord start, boolean preserveWindow) { - if (start == null || !start.mVisibleRequested) { - return; - } - reset(preserveWindow); - - final PooledFunction f = PooledLambda.obtainFunction( - EnsureVisibleActivitiesConfigHelper::processActivity, this, - PooledLambda.__(ActivityRecord.class)); - forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/); - f.recycle(); - - if (mUpdateConfig) { - // Ensure the resumed state of the focus activity if we updated the configuration of - // any activity. - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - } - - boolean processActivity(ActivityRecord r) { - mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow); - mBehindFullscreen |= r.occludesParent(); - return mBehindFullscreen; - } - } - - private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper = - new CheckBehindFullscreenActivityHelper(); - private class CheckBehindFullscreenActivityHelper { - private boolean mAboveTop; - private boolean mBehindFullscreenActivity; - private ActivityRecord mToCheck; - private Consumer<ActivityRecord> mHandleBehindFullscreenActivity; - private boolean mHandlingOccluded; - - private void reset(ActivityRecord toCheck, - Consumer<ActivityRecord> handleBehindFullscreenActivity) { - mToCheck = toCheck; - mHandleBehindFullscreenActivity = handleBehindFullscreenActivity; - mAboveTop = true; - mBehindFullscreenActivity = false; - - if (!shouldBeVisible(null)) { - // The root task is not visible, so no activity in it should be displaying a - // starting window. Mark all activities below top and behind fullscreen. - mAboveTop = false; - mBehindFullscreenActivity = true; - } - - mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null; - } - - boolean process(ActivityRecord toCheck, - Consumer<ActivityRecord> handleBehindFullscreenActivity) { - reset(toCheck, handleBehindFullscreenActivity); - - if (!mHandlingOccluded && mBehindFullscreenActivity) { - return true; - } - - final ActivityRecord topActivity = topRunningActivity(); - final PooledFunction f = PooledLambda.obtainFunction( - CheckBehindFullscreenActivityHelper::processActivity, this, - PooledLambda.__(ActivityRecord.class), topActivity); - forAllActivities(f); - f.recycle(); - - return mBehindFullscreenActivity; - } - - /** Returns {@code true} to stop the outer loop and indicate the result is computed. */ - private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) { - if (mAboveTop) { - if (r == topActivity) { - if (r == mToCheck) { - // It is the top activity in a visible root task. - mBehindFullscreenActivity = false; - return true; - } - mAboveTop = false; - } - mBehindFullscreenActivity |= r.occludesParent(); - return false; - } - - if (mHandlingOccluded) { - // Iterating through all occluded activities. - if (mBehindFullscreenActivity) { - mHandleBehindFullscreenActivity.accept(r); - } - } else if (r == mToCheck) { - return true; - } else if (mBehindFullscreenActivity) { - // It is occluded before {@param toCheck} is found. - return true; - } - mBehindFullscreenActivity |= r.occludesParent(); - return false; - } - } private final FindRootHelper mFindRootHelper = new FindRootHelper(); private class FindRootHelper { @@ -819,18 +602,6 @@ class Task extends WindowContainer<WindowContainer> { */ private boolean mForceNotOrganized; - /** - * This task was created by the task organizer which has the following implementations. - * <ul> - * <lis>The task won't be removed when it is empty. Removal has to be an explicit request - * from the task organizer.</li> - * <li>Unlike other non-root tasks, it's direct children are visible to the task - * organizer for ordering purposes.</li> - * </ul> - */ - @VisibleForTesting - boolean mCreatedByOrganizer; - // Tracking cookie for the creation of this task. IBinder mLaunchCookie; @@ -858,11 +629,8 @@ class Task extends WindowContainer<WindowContainer> { IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear, boolean _removeWithTaskOrganizer) { - super(atmService.mWindowManager); + super(atmService, _createdByOrganizer); - mAtmService = atmService; - mTaskSupervisor = atmService.mTaskSupervisor; - mRootWindowContainer = mAtmService.mRootWindowContainer; mTaskId = _taskId; mUserId = _userId; mResizeMode = resizeMode; @@ -913,7 +681,6 @@ class Task extends WindowContainer<WindowContainer> { mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper); mCurrentUser = mAtmService.mAmInternal.getCurrentUserId(); - mCreatedByOrganizer = _createdByOrganizer; mLaunchCookie = _launchCookie; mDeferTaskAppear = _deferTaskAppear; mRemoveWithTaskOrganizer = _removeWithTaskOrganizer; @@ -1472,64 +1239,80 @@ class Task extends WindowContainer<WindowContainer> { } } - void cleanUpActivityReferences(ActivityRecord r) { - // mPausingActivity is set at leaf task - if (mPausingActivity != null && mPausingActivity == r) { - mPausingActivity = null; - } - - if (mResumedActivity != null && mResumedActivity == r) { - setResumedActivity(null, "cleanUpActivityReferences"); - } - - final WindowContainer parent = getParent(); - if (parent != null && parent.asTask() != null) { - parent.asTask().cleanUpActivityReferences(r); - return; + /** Returns the currently topmost resumed activity. */ + @Nullable + ActivityRecord getTopResumedActivity() { + if (!isLeafTask()) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + ActivityRecord resumedActivity = mChildren.get(i).asTask().getTopResumedActivity(); + if (resumedActivity != null) { + return resumedActivity; + } + } } - r.removeTimeouts(); - mExitingActivities.remove(r); - } - /** @return the currently resumed activity. */ - ActivityRecord getResumedActivity() { - if (isLeafTask()) { - return mResumedActivity; + final ActivityRecord taskResumedActivity = getResumedActivity(); + ActivityRecord topResumedActivity = null; + for (int i = mChildren.size() - 1; i >= 0; --i) { + final WindowContainer child = mChildren.get(i); + if (child.asTaskFragment() != null) { + final ActivityRecord[] resumedActivity = new ActivityRecord[1]; + child.asTaskFragment().forAllLeafTaskFragments(fragment -> { + if (fragment.getResumedActivity() != null) { + resumedActivity[0] = fragment.getResumedActivity(); + return true; + } + return false; + }); + topResumedActivity = resumedActivity[0]; + } else if (taskResumedActivity != null + && child.asActivityRecord() == taskResumedActivity) { + topResumedActivity = taskResumedActivity; + } + if (topResumedActivity != null) { + return topResumedActivity; + } } - - final Task task = getTask(t -> t.mResumedActivity != null, true /* traverseTopToBottom */); - return task != null ? task.mResumedActivity : null; - } - - @VisibleForTesting - void setPausingActivity(ActivityRecord pausing) { - mPausingActivity = pausing; + return null; } /** - * @return the currently pausing activity of this task or the topmost pausing activity of the - * child tasks + * Returns the currently topmost pausing activity. */ - ActivityRecord getPausingActivity() { - if (isLeafTask()) { - return mPausingActivity; + @Nullable + ActivityRecord getTopPausingActivity() { + if (!isLeafTask()) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + ActivityRecord pausingActivity = mChildren.get(i).asTask().getTopPausingActivity(); + if (pausingActivity != null) { + return pausingActivity; + } + } } - final Task task = getTask(t -> t.mPausingActivity != null, true /* traverseTopToBottom */); - return task != null ? task.mPausingActivity : null; - } - - void setResumedActivity(ActivityRecord r, String reason) { - warnForNonLeafTask("setResumedActivity"); - if (mResumedActivity == r) { - return; + final ActivityRecord taskPausingActivity = getPausingActivity(); + ActivityRecord topPausingActivity = null; + for (int i = mChildren.size() - 1; i >= 0; --i) { + final WindowContainer child = mChildren.get(i); + if (child.asTaskFragment() != null) { + final ActivityRecord[] pausingActivity = new ActivityRecord[1]; + child.asTaskFragment().forAllLeafTaskFragments(fragment -> { + if (fragment.getPausingActivity() != null) { + pausingActivity[0] = fragment.getPausingActivity(); + return true; + } + return false; + }); + topPausingActivity = pausingActivity[0]; + } else if (taskPausingActivity != null + && child.asActivityRecord() == taskPausingActivity) { + topPausingActivity = taskPausingActivity; + } + if (topPausingActivity != null) { + return topPausingActivity; + } } - - if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK, - "setResumedActivity task:" + this + " + from: " - + mResumedActivity + " to:" + r + " reason:" + reason); - mResumedActivity = r; - mTaskSupervisor.updateTopResumedActivityIfNeeded(); + return null; } void updateTaskMovement(boolean toTop, int position) { @@ -1568,11 +1351,6 @@ class Task extends WindowContainer<WindowContainer> { mTaskId, mUserId); } - void setAdjacentTask(Task adjacent) { - mAdjacentTask = adjacent; - adjacent.mAdjacentTask = this; - } - void setTaskToAffiliateWith(Task taskToAffiliateWith) { closeRecentsChain(); mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId; @@ -1681,22 +1459,7 @@ class Task extends WindowContainer<WindowContainer> { } @Override - public int getActivityType() { - final int applicationType = super.getActivityType(); - if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) { - return applicationType; - } - return getTopChild().getActivityType(); - } - - @Override void addChild(WindowContainer child, int index) { - // If this task had any child before we added this one. - boolean hadChild = hasChild(); - // getActivityType() looks at the top child, so we need to read the type before adding - // a new child in case the new child is on top and UNDEFINED. - final int activityType = getActivityType(); - index = getAdjustedChildPosition(child, index); super.addChild(child, index); @@ -1712,10 +1475,17 @@ class Task extends WindowContainer<WindowContainer> { // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); - final ActivityRecord r = child.asActivityRecord(); - if (r == null) return; + // Only pass minimum dimensions for pure TaskFragment. Task's minimum dimensions must be + // passed from Task constructor. + final TaskFragment childTaskFrag = child.asTaskFragment(); + if (childTaskFrag != null && childTaskFrag.asTask() == null) { + childTaskFrag.setMinDimensions(mMinWidth, mMinHeight); + } + } - r.inHistory = true; + /** Called when an {@link ActivityRecord} is added as a descendant */ + void onDescendantActivityAdded(boolean hadChild, int activityType, ActivityRecord r) { + warnForNonLeafTask("onDescendantActivityAdded"); // Only set this based on the first activity if (!hadChild) { @@ -1742,10 +1512,6 @@ class Task extends WindowContainer<WindowContainer> { updateEffectiveIntent(); } - void addChild(ActivityRecord r) { - addChild(r, Integer.MAX_VALUE /* add on top */); - } - @Override void removeChild(WindowContainer child) { removeChild(child, "removeChild"); @@ -1991,32 +1757,6 @@ class Task extends WindowContainer<WindowContainer> { && supportsMultiWindowInDisplayArea(tda); } - boolean supportsMultiWindow() { - return supportsMultiWindowInDisplayArea(getDisplayArea()); - } - - /** - * @return whether this task supports multi-window if it is in the given - * {@link TaskDisplayArea}. - */ - boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) { - if (!mAtmService.mSupportsMultiWindow) { - return false; - } - if (tda == null) { - Slog.w(TAG_TASKS, "Can't find TaskDisplayArea to determine support for multi" - + " window. Task id=" + mTaskId + " attached=" + isAttached()); - return false; - } - - if (!isResizeable() && !tda.supportsNonResizableMultiWindow()) { - // Not support non-resizable in multi window. - return false; - } - - return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight); - } - /** * Check whether this task can be launched on the specified display. * @@ -2152,60 +1892,6 @@ class Task extends WindowContainer<WindowContainer> { } } - void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds, - @NonNull Configuration parentConfig) { - int minWidth = mMinWidth; - int minHeight = mMinHeight; - // If the task has no requested minimal size, we'd like to enforce a minimal size - // so that the user can not render the task too small to manipulate. We don't need - // to do this for the root pinned task as the bounds are controlled by the system. - if (!inPinnedWindowingMode()) { - final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp; - final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT; - final int defaultMinSize = (int) (defaultMinSizeDp * density); - - if (minWidth == INVALID_MIN_SIZE) { - minWidth = defaultMinSize; - } - if (minHeight == INVALID_MIN_SIZE) { - minHeight = defaultMinSize; - } - } - if (bounds.isEmpty()) { - // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they - // do, we can just skip. - final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); - if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) { - return; - } - bounds.set(parentBounds); - } - final boolean adjustWidth = minWidth > bounds.width(); - final boolean adjustHeight = minHeight > bounds.height(); - if (!(adjustWidth || adjustHeight)) { - return; - } - - if (adjustWidth) { - if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) { - bounds.left = bounds.right - minWidth; - } else { - // Either left bounds match, or neither match, or the previous bounds were - // fullscreen and we default to keeping left. - bounds.right = bounds.left + minWidth; - } - } - if (adjustHeight) { - if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) { - bounds.top = bounds.bottom - minHeight; - } else { - // Either top bounds match, or neither match, or the previous bounds were - // fullscreen and we default to keeping top. - bounds.bottom = bounds.top + minHeight; - } - } - } - void setLastNonFullscreenBounds(Rect bounds) { if (mLastNonFullscreenBounds == null) { mLastNonFullscreenBounds = new Rect(bounds); @@ -2214,32 +1900,6 @@ class Task extends WindowContainer<WindowContainer> { } } - /** - * This should be called when an child activity changes state. This should only - * be called from - * {@link ActivityRecord#setState(ActivityState, String)} . - * @param record The {@link ActivityRecord} whose state has changed. - * @param state The new state. - * @param reason The reason for the change. - */ - void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) { - warnForNonLeafTask("onActivityStateChanged"); - if (record == mResumedActivity && state != RESUMED) { - setResumedActivity(null, reason + " - onActivityStateChanged"); - } - - if (state == RESUMED) { - if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { - Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason); - } - setResumedActivity(record, reason + " - onActivityStateChanged"); - if (record == mRootWindowContainer.getTopResumedActivity()) { - mAtmService.setResumedActivityUncheckLocked(record, reason); - } - mTaskSupervisor.mRecentTasks.add(record.getTask()); - } - } - private void onConfigurationChangedInner(Configuration newParentConfig) { // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so // restore the last recorded non-fullscreen bounds. @@ -2532,400 +2192,6 @@ class Task extends WindowContainer<WindowContainer> { mTaskSupervisor.mLaunchParamsPersister.saveTask(this, display); } - /** - * Adjust bounds to stay within root task bounds. - * - * Since bounds might be outside of root task bounds, this method tries to move the bounds in - * a way that keep them unchanged, but be contained within the root task bounds. - * - * @param bounds Bounds to be adjusted. - * @param rootTaskBounds Bounds within which the other bounds should remain. - * @param overlapPxX The amount of px required to be visible in the X dimension. - * @param overlapPxY The amount of px required to be visible in the Y dimension. - */ - private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX, - int overlapPxY) { - if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) { - return; - } - - // For each side of the parent (eg. left), check if the opposing side of the window (eg. - // right) is at least overlap pixels away. If less, offset the window by that difference. - int horizontalDiff = 0; - // If window is smaller than overlap, use it's smallest dimension instead - int overlapLR = Math.min(overlapPxX, bounds.width()); - if (bounds.right < (rootTaskBounds.left + overlapLR)) { - horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left); - } else if (bounds.left > (rootTaskBounds.right - overlapLR)) { - horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left)); - } - int verticalDiff = 0; - int overlapTB = Math.min(overlapPxY, bounds.width()); - if (bounds.bottom < (rootTaskBounds.top + overlapTB)) { - verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top); - } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) { - verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top)); - } - bounds.offset(horizontalDiff, verticalDiff); - } - - /** - * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than - * intersectBounds on a side, then the respective side will not be intersected. - * - * The assumption is that if inOutBounds is initially larger than intersectBounds, then the - * inset on that side is no-longer applicable. This scenario happens when a task's minimal - * bounds are larger than the provided parent/display bounds. - * - * @param inOutBounds the bounds to intersect. - * @param intersectBounds the bounds to intersect with. - * @param intersectInsets insets to apply to intersectBounds before intersecting. - */ - static void intersectWithInsetsIfFits( - Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) { - if (inOutBounds.right <= intersectBounds.right) { - inOutBounds.right = - Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right); - } - if (inOutBounds.bottom <= intersectBounds.bottom) { - inOutBounds.bottom = - Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom); - } - if (inOutBounds.left >= intersectBounds.left) { - inOutBounds.left = - Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left); - } - if (inOutBounds.top >= intersectBounds.top) { - inOutBounds.top = - Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top); - } - } - - /** - * Gets bounds with non-decor and stable insets applied respectively. - * - * If bounds overhangs the display, those edges will not get insets. See - * {@link #intersectWithInsetsIfFits} - * - * @param outNonDecorBounds where to place bounds with non-decor insets applied. - * @param outStableBounds where to place bounds with stable insets applied. - * @param bounds the bounds to inset. - */ - private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds, - DisplayInfo displayInfo) { - outNonDecorBounds.set(bounds); - outStableBounds.set(bounds); - final Task rootTask = getRootTask(); - if (rootTask == null || rootTask.mDisplayContent == null) { - return; - } - mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); - - final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy(); - policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, - displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets); - intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets); - - policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation); - intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets); - } - - /** - * Forces the app bounds related configuration can be computed by - * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo, - * ActivityRecord.CompatDisplayInsets)}. - */ - private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) { - final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds(); - if (appBounds != null) { - appBounds.setEmpty(); - } - inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED; - inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED; - } - - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) { - if (overrideDisplayInfo != null) { - // Make sure the screen related configs can be computed by the provided display info. - inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; - invalidateAppBoundsConfig(inOutConfig); - } - computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo, - null /* compatInsets */); - } - - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig) { - computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, - null /* compatInsets */); - } - - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig, - @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { - if (compatInsets != null) { - // Make sure the app bounds can be computed by the compat insets. - invalidateAppBoundsConfig(inOutConfig); - } - computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, - compatInsets); - } - - /** - * Calculates configuration values used by the client to get resources. This should be run - * using app-facing bounds (bounds unmodified by animations or transient interactions). - * - * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely - * configuring an "inherit-bounds" window which means that all configuration settings would - * just be inherited from the parent configuration. - **/ - void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo, - @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { - int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); - if (windowingMode == WINDOWING_MODE_UNDEFINED) { - windowingMode = parentConfig.windowConfiguration.getWindowingMode(); - } - - float density = inOutConfig.densityDpi; - if (density == Configuration.DENSITY_DPI_UNDEFINED) { - density = parentConfig.densityDpi; - } - density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; - - // The bounds may have been overridden at this level. If the parent cannot cover these - // bounds, the configuration is still computed according to the override bounds. - final boolean insideParentBounds; - - final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); - final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds(); - if (resolvedBounds == null || resolvedBounds.isEmpty()) { - mTmpFullBounds.set(parentBounds); - insideParentBounds = true; - } else { - mTmpFullBounds.set(resolvedBounds); - insideParentBounds = parentBounds.contains(resolvedBounds); - } - - // Non-null compatibility insets means the activity prefers to keep its original size, so - // out bounds doesn't need to be restricted by the parent or current display - final boolean customContainerPolicy = compatInsets != null; - - Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); - if (outAppBounds == null || outAppBounds.isEmpty()) { - // App-bounds hasn't been overridden, so calculate a value for it. - inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds); - outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); - - if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { - final Rect containingAppBounds; - if (insideParentBounds) { - containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); - } else { - // Restrict appBounds to display non-decor rather than parent because the - // override bounds are beyond the parent. Otherwise, it won't match the - // overridden bounds. - final TaskDisplayArea displayArea = getDisplayArea(); - containingAppBounds = displayArea != null - ? displayArea.getWindowConfiguration().getAppBounds() : null; - } - if (containingAppBounds != null && !containingAppBounds.isEmpty()) { - outAppBounds.intersect(containingAppBounds); - } - } - } - - if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED - || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) { - mTmpNonDecorBounds.set(mTmpFullBounds); - mTmpStableBounds.set(mTmpFullBounds); - } else if (!customContainerPolicy - && (overrideDisplayInfo != null || getDisplayContent() != null)) { - final DisplayInfo di = overrideDisplayInfo != null - ? overrideDisplayInfo - : getDisplayContent().getDisplayInfo(); - - // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen - // area, i.e. the screen area without the system bars. - // The non decor inset are areas that could never be removed in Honeycomb. See - // {@link WindowManagerPolicy#getNonDecorInsetsLw}. - calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di); - } else { - // Apply the given non-decor and stable insets to calculate the corresponding bounds - // for screen size of configuration. - int rotation = inOutConfig.windowConfiguration.getRotation(); - if (rotation == ROTATION_UNDEFINED) { - rotation = parentConfig.windowConfiguration.getRotation(); - } - if (rotation != ROTATION_UNDEFINED && customContainerPolicy) { - mTmpNonDecorBounds.set(mTmpFullBounds); - mTmpStableBounds.set(mTmpFullBounds); - compatInsets.getBoundsByRotation(mTmpBounds, rotation); - intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds, - compatInsets.mNonDecorInsets[rotation]); - intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds, - compatInsets.mStableInsets[rotation]); - outAppBounds.set(mTmpNonDecorBounds); - } else { - // Set to app bounds because it excludes decor insets. - mTmpNonDecorBounds.set(outAppBounds); - mTmpStableBounds.set(outAppBounds); - } - } - - if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density); - inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy) - ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp) - : overrideScreenWidthDp; - } - if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); - inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy) - ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp) - : overrideScreenHeightDp; - } - - if (inOutConfig.smallestScreenWidthDp - == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { - if (WindowConfiguration.isFloating(windowingMode)) { - // For floating tasks, calculate the smallest width from the bounds of the task - inOutConfig.smallestScreenWidthDp = (int) ( - Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density); - } - // otherwise, it will just inherit - } - } - - if (inOutConfig.orientation == ORIENTATION_UNDEFINED) { - inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp) - ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; - } - if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) { - // For calculating screen layout, we need to use the non-decor inset screen area for the - // calculation for compatibility reasons, i.e. screen area without system bars that - // could never go away in Honeycomb. - int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density); - int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density); - // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is - // undefined so it can't be used. - if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - compatScreenWidthDp = inOutConfig.screenWidthDp; - } - if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - compatScreenHeightDp = inOutConfig.screenHeightDp; - } - // Reducing the screen layout starting from its parent config. - inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout, - compatScreenWidthDp, compatScreenHeightDp); - } - } - - /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */ - static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp, - int screenHeightDp) { - sourceScreenLayout = sourceScreenLayout - & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK); - final int longSize = Math.max(screenWidthDp, screenHeightDp); - final int shortSize = Math.min(screenWidthDp, screenHeightDp); - return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize); - } - - @Override - void resolveOverrideConfiguration(Configuration newParentConfig) { - mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); - super.resolveOverrideConfiguration(newParentConfig); - - int windowingMode = - getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); - final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode(); - - // Resolve override windowing mode to fullscreen for home task (even on freeform - // display), or split-screen if in split-screen mode. - if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) { - windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode) - ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN; - getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); - } - - // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in - // pinned windowing mode. - if (!supportsMultiWindow()) { - final int candidateWindowingMode = - windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode; - if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode) - && candidateWindowingMode != WINDOWING_MODE_PINNED) { - getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode( - WINDOWING_MODE_FULLSCREEN); - } - } - - if (isLeafTask()) { - resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */); - } - computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); - } - - private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig, - Rect previousBounds) { - - int windowingMode = - getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); - if (windowingMode == WINDOWING_MODE_UNDEFINED) { - windowingMode = newParentConfig.windowConfiguration.getWindowingMode(); - } - // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old - // mode that may cause the bounds to be miscalculated, e.g. letterboxed. - getConfiguration().windowConfiguration.setWindowingMode(windowingMode); - Rect outOverrideBounds = - getResolvedOverrideConfiguration().windowConfiguration.getBounds(); - - if (windowingMode == WINDOWING_MODE_FULLSCREEN) { - // Use empty bounds to indicate "fill parent". - outOverrideBounds.setEmpty(); - // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if - // the parent or display is smaller than the size, the content may be cropped. - return; - } - - adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig); - if (windowingMode == WINDOWING_MODE_FREEFORM) { - computeFreeformBounds(outOverrideBounds, newParentConfig); - return; - } - } - - /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */ - private void computeFreeformBounds(@NonNull Rect outBounds, - @NonNull Configuration newParentConfig) { - // by policy, make sure the window remains within parent somewhere - final float density = - ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT; - final Rect parentBounds = - new Rect(newParentConfig.windowConfiguration.getBounds()); - final DisplayContent display = getDisplayContent(); - if (display != null) { - // If a freeform window moves below system bar, there is no way to move it again - // by touch. Because its caption is covered by system bar. So we exclude them - // from root task bounds. and then caption will be shown inside stable area. - final Rect stableBounds = new Rect(); - display.getStableRect(stableBounds); - parentBounds.intersect(stableBounds); - } - - fitWithinBounds(outBounds, parentBounds, - (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP), - (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP)); - - // Prevent to overlap caption with stable insets. - final int offsetTop = parentBounds.top - outBounds.top; - if (offsetTop > 0) { - outBounds.offset(0, offsetTop); - } - } - Rect updateOverrideConfigurationFromLaunchBounds() { // If the task is controlled by another organized task, do not set override // configurations and let its parent (organized task) to control it; @@ -2974,22 +2240,14 @@ class Task extends WindowContainer<WindowContainer> { } } - int getDisplayId() { - final DisplayContent dc = getDisplayContent(); - return dc != null ? dc.mDisplayId : INVALID_DISPLAY; - } - /** @return Id of root task. */ int getRootTaskId() { return getRootTask().mTaskId; } + @Nullable Task getRootTask() { - final WindowContainer parent = getParent(); - if (parent == null) return this; - - final Task parentTask = parent.asTask(); - return parentTask == null ? this : parentTask.getRootTask(); + return getRootTaskFragment().asTask(); } /** @return the first organized task. */ @@ -3104,12 +2362,12 @@ class Task extends WindowContainer<WindowContainer> { // and focused application if needed. focusableTask.moveToFront(myReason); // Top display focused root task is changed, update top resumed activity if needed. - if (rootTask.getResumedActivity() != null) { + if (rootTask.getTopResumedActivity() != null) { mTaskSupervisor.updateTopResumedActivityIfNeeded(); // Set focused app directly because if the next focused activity is already resumed // (e.g. the next top activity is on a different display), there won't have activity // state change to update it. - mAtmService.setResumedActivityUncheckLocked(rootTask.getResumedActivity(), reason); + mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason); } return rootTask; } @@ -3158,17 +2416,16 @@ class Task extends WindowContainer<WindowContainer> { // Figure-out min/max possible position depending on if child can show for current user. int minPosition = (canShowChild) ? computeMinUserPosition(0, size) : 0; - int maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1); - if (!hasChild(wc)) { - // Increase the maxPosition because children size will grow once wc is added. - ++maxPosition; + int maxPosition = minPosition; + if (size > 0) { + maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1); } // Factor in always-on-top children in max possible position. if (!wc.isAlwaysOnTop()) { // We want to place all non-always-on-top containers below always-on-top ones. while (maxPosition > minPosition) { - if (!mChildren.get(maxPosition - 1).isAlwaysOnTop()) break; + if (!mChildren.get(maxPosition).isAlwaysOnTop()) break; --maxPosition; } } @@ -3179,6 +2436,12 @@ class Task extends WindowContainer<WindowContainer> { } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) { return POSITION_TOP; } + + // Increase the maxPosition because children size will grow once wc is added. + if (!hasChild(wc)) { + ++maxPosition; + } + // Reset position based on minimum/maximum possible positions. return Math.min(Math.max(suggestedPosition, minPosition), maxPosition); } @@ -3560,18 +2823,6 @@ class Task extends WindowContainer<WindowContainer> { mForceShowForAllUsers = forceShowForAllUsers; } - @Override - public boolean isAttached() { - final TaskDisplayArea taskDisplayArea = getDisplayArea(); - return taskDisplayArea != null && !taskDisplayArea.isRemoved(); - } - - @Override - @Nullable - TaskDisplayArea getDisplayArea() { - return (TaskDisplayArea) super.getDisplayArea(); - } - /** * When we are in a floating root task (Freeform, Pinned, ...) we calculate * insets differently. However if we are animating to the fullscreen root task @@ -3582,45 +2833,6 @@ class Task extends WindowContainer<WindowContainer> { return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState; } - /** - * Returns true if the root task is translucent and can have other contents visible behind it if - * needed. A root task is considered translucent if it don't contain a visible or - * starting (about to be visible) activity that is fullscreen (opaque). - * @param starting The currently starting activity or null if there is none. - */ - @VisibleForTesting - boolean isTranslucent(ActivityRecord starting) { - if (!isAttached() || isForceHidden()) { - return true; - } - final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity, - PooledLambda.__(ActivityRecord.class), starting); - final ActivityRecord opaque = getActivity(p); - p.recycle(); - return opaque == null; - } - - private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) { - if (r.finishing) { - // We don't factor in finishing activities when determining translucency since - // they will be gone soon. - return false; - } - - if (!r.visibleIgnoringKeyguard && r != starting) { - // Also ignore invisible activities that are not the currently starting - // activity (about to be visible). - return false; - } - - if (r.occludesParent()) { - // Root task isn't translucent if it has at least one fullscreen activity - // that is visible. - return true; - } - return false; - } - /** Returns the top-most activity that occludes the given one, or {@code null} if none. */ @Nullable ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { @@ -3714,19 +2926,6 @@ class Task extends WindowContainer<WindowContainer> { return activity != null ? activity.findMainWindow() : null; } - ActivityRecord topRunningActivity() { - return topRunningActivity(false /* focusableOnly */); - } - - ActivityRecord topRunningActivity(boolean focusableOnly) { - // Split into 2 to avoid object creation due to variable capture. - if (focusableOnly) { - return getActivity((r) -> r.canBeTopRunning() && r.isFocusable()); - } else { - return getActivity(ActivityRecord::canBeTopRunning); - } - } - ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) { final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTopRunningNonDelayed , PooledLambda.__(ActivityRecord.class), notTop); @@ -3781,12 +2980,6 @@ class Task extends WindowContainer<WindowContainer> { }); } - boolean isTopActivityFocusable() { - final ActivityRecord r = topRunningActivity(); - return r != null ? r.isFocusable() - : (isFocusable() && getWindowConfiguration().canReceiveKeys()); - } - boolean isFocusableAndVisible() { return isTopActivityFocusable() && shouldBeVisible(null /* starting */); } @@ -3902,6 +3095,41 @@ class Task extends WindowContainer<WindowContainer> { return false; } + /** Iterates through all leaf task fragments and the leaf tasks. */ + void forAllLeafTasksAndLeafTaskFragments(final Consumer<TaskFragment> callback, + boolean traverseTopToBottom) { + forAllLeafTasks(task -> { + if (task.isLeafTaskFragment()) { + callback.accept(task); + return; + } + + // A leaf task that may contains both activities and task fragments. + boolean consumed = false; + if (traverseTopToBottom) { + for (int i = task.mChildren.size() - 1; i >= 0; --i) { + final WindowContainer child = mChildren.get(i); + if (child.asTaskFragment() != null) { + child.forAllLeafTaskFragments(callback, traverseTopToBottom); + } else if (child.asActivityRecord() != null && !consumed) { + callback.accept(task); + consumed = true; + } + } + } else { + for (int i = 0; i < task.mChildren.size(); i++) { + final WindowContainer child = mChildren.get(i); + if (child.asTaskFragment() != null) { + child.forAllLeafTaskFragments(callback, traverseTopToBottom); + } else if (child.asActivityRecord() != null && !consumed) { + callback.accept(task); + consumed = true; + } + } + } + }, traverseTopToBottom); + } + @Override boolean forAllRootTasks(Function<Task, Boolean> callback, boolean traverseTopToBottom) { return isRootTask() ? callback.apply(this) : false; @@ -4072,6 +3300,9 @@ class Task extends WindowContainer<WindowContainer> { info.userId = isLeafTask() ? mUserId : mCurrentUser; info.taskId = mTaskId; info.displayId = getDisplayId(); + if (tda != null) { + info.displayAreaFeatureId = tda.mFeatureId; + } info.isRunning = getTopNonFinishingActivity() != null; final Intent baseIntent = getBaseIntent(); // Make a copy of base intent because this is like a snapshot info. @@ -4133,6 +3364,7 @@ class Task extends WindowContainer<WindowContainer> { : INVALID_TASK_ID; info.isFocused = isFocused(); info.isVisible = hasVisibleChildren(); + info.isSleeping = shouldSleepActivities(); ActivityRecord topRecord = getTopNonFinishingActivity(); info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null; } @@ -4200,184 +3432,6 @@ class Task extends WindowContainer<WindowContainer> { return this; } - /** - * Returns true if the task should be visible. - * - * @param starting The currently starting activity or null if there is none. - */ - boolean shouldBeVisible(ActivityRecord starting) { - return getVisibility(starting) != TASK_VISIBILITY_INVISIBLE; - } - - /** - * Returns true if the task should be visible. - * - * @param starting The currently starting activity or null if there is none. - */ - @TaskVisibility - int getVisibility(ActivityRecord starting) { - if (!isAttached() || isForceHidden()) { - return TASK_VISIBILITY_INVISIBLE; - } - - if (isTopActivityLaunchedBehind()) { - return TASK_VISIBILITY_VISIBLE; - } - - boolean gotRootSplitScreenTask = false; - boolean gotOpaqueSplitScreenPrimary = false; - boolean gotOpaqueSplitScreenSecondary = false; - boolean gotTranslucentFullscreen = false; - boolean gotTranslucentSplitScreenPrimary = false; - boolean gotTranslucentSplitScreenSecondary = false; - boolean shouldBeVisible = true; - - // This root task is only considered visible if all its parent root tasks are considered - // visible, so check the visibility of all ancestor root task first. - final WindowContainer parent = getParent(); - if (parent.asTask() != null) { - final int parentVisibility = parent.asTask().getVisibility(starting); - if (parentVisibility == TASK_VISIBILITY_INVISIBLE) { - // Can't be visible if parent isn't visible - return TASK_VISIBILITY_INVISIBLE; - } else if (parentVisibility == TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) { - // Parent is behind a translucent container so the highest visibility this container - // can get is that. - gotTranslucentFullscreen = true; - } - } - - final List<Task> adjacentTasks = new ArrayList<>(); - final int windowingMode = getWindowingMode(); - final boolean isAssistantType = isActivityTypeAssistant(); - for (int i = parent.getChildCount() - 1; i >= 0; --i) { - final WindowContainer wc = parent.getChildAt(i); - final Task other = wc.asTask(); - if (other == null) continue; - - final boolean hasRunningActivities = other.topRunningActivity() != null; - if (other == this) { - // Should be visible if there is no other stack occluding it, unless it doesn't - // have any running activities, not starting one and not home stack. - shouldBeVisible = hasRunningActivities || isInTask(starting) != null - || isActivityTypeHome(); - break; - } - - if (!hasRunningActivities) { - continue; - } - - final int otherWindowingMode = other.getWindowingMode(); - - if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) { - if (other.isTranslucent(starting)) { - // Can be visible behind a translucent fullscreen stack. - gotTranslucentFullscreen = true; - continue; - } - return TASK_VISIBILITY_INVISIBLE; - } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW - && other.matchParentBounds()) { - if (other.isTranslucent(starting)) { - // Can be visible behind a translucent task. - gotTranslucentFullscreen = true; - continue; - } - // Multi-window task that matches parent bounds would occlude other children. - return TASK_VISIBILITY_INVISIBLE; - } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - && !gotOpaqueSplitScreenPrimary) { - gotRootSplitScreenTask = true; - gotTranslucentSplitScreenPrimary = other.isTranslucent(starting); - gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary; - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - && gotOpaqueSplitScreenPrimary) { - // Can not be visible behind another opaque stack in split-screen-primary mode. - return TASK_VISIBILITY_INVISIBLE; - } - } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY - && !gotOpaqueSplitScreenSecondary) { - gotRootSplitScreenTask = true; - gotTranslucentSplitScreenSecondary = other.isTranslucent(starting); - gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary; - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY - && gotOpaqueSplitScreenSecondary) { - // Can not be visible behind another opaque stack in split-screen-secondary mode. - return TASK_VISIBILITY_INVISIBLE; - } - } - if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) { - // Can not be visible if we are in split-screen windowing mode and both halves of - // the screen are opaque. - return TASK_VISIBILITY_INVISIBLE; - } - if (isAssistantType && gotRootSplitScreenTask) { - // Assistant stack can't be visible behind split-screen. In addition to this not - // making sense, it also works around an issue here we boost the z-order of the - // assistant window surfaces in window manager whenever it is visible. - return TASK_VISIBILITY_INVISIBLE; - } - if (other.mAdjacentTask != null) { - if (adjacentTasks.contains(other.mAdjacentTask)) { - if (other.isTranslucent(starting) - || other.mAdjacentTask.isTranslucent(starting)) { - // Can be visible behind a translucent adjacent tasks. - gotTranslucentFullscreen = true; - continue; - } - // Can not be visible behind adjacent tasks. - return TASK_VISIBILITY_INVISIBLE; - } else { - adjacentTasks.add(other); - } - } - } - - if (!shouldBeVisible) { - return TASK_VISIBILITY_INVISIBLE; - } - - // Handle cases when there can be a translucent split-screen stack on top. - switch (windowingMode) { - case WINDOWING_MODE_FULLSCREEN: - if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) { - // At least one of the split-screen stacks that covers this one is translucent. - // When in split mode, home task will be reparented to the secondary split while - // leaving tasks not supporting split below. Due to - // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to - // the bottom, this makes sure tasks not in split roots won't occlude home task - // unexpectedly. - return TASK_VISIBILITY_INVISIBLE; - } - break; - case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - if (gotTranslucentSplitScreenPrimary) { - // Covered by translucent primary split-screen on top. - return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; - } - break; - case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: - if (gotTranslucentSplitScreenSecondary) { - // Covered by translucent secondary split-screen on top. - return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; - } - break; - } - - // Lastly - check if there is a translucent fullscreen stack on top. - return gotTranslucentFullscreen ? TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT - : TASK_VISIBILITY_VISIBLE; - } - - private boolean isTopActivityLaunchedBehind() { - final ActivityRecord top = topRunningActivity(); - if (top != null && top.mLaunchTaskBehind) { - return true; - } - return false; - } - ActivityRecord isInTask(ActivityRecord r) { if (r == null) { return null; @@ -4560,7 +3614,7 @@ class Task extends WindowContainer<WindowContainer> { // Increment the total number of non-finishing activities numActivities++; - if (top == null || (top.isState(ActivityState.INITIALIZING))) { + if (top == null || (top.isState(INITIALIZING))) { top = r; // Reset the number of running activities until we hit the first non-initializing // activity @@ -5303,9 +4357,7 @@ class Task extends WindowContainer<WindowContainer> { return super.isAlwaysOnTop(); } - /** - * Returns whether this task is currently forced to be hidden for any reason. - */ + @Override protected boolean isForceHidden() { return mForceHiddenFlags != 0; } @@ -5592,19 +4644,6 @@ class Task extends WindowContainer<WindowContainer> { r.completeResumeLocked(); } - void awakeFromSleepingLocked() { - if (!isLeafTask()) { - forAllLeafTasks((task) -> task.awakeFromSleepingLocked(), - true /* traverseTopToBottom */); - return; - } - - if (mPausingActivity != null) { - Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause"); - mPausingActivity.activityPaused(true); - } - } - void checkReadyForSleep() { if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) { mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */); @@ -5623,302 +4662,13 @@ class Task extends WindowContainer<WindowContainer> { * the process of going to sleep (checkReadyForSleep will be called when that process finishes). */ boolean goToSleepIfPossible(boolean shuttingDown) { - if (!isLeafTask()) { - final int[] sleepInProgress = {0}; - forAllLeafTasks((t) -> { - if (!t.goToSleepIfPossible(shuttingDown)) { - sleepInProgress[0]++; - } - }, true); - return sleepInProgress[0] == 0; - } - - boolean shouldSleep = true; - if (mResumedActivity != null) { - // Still have something resumed; can't sleep until it is paused. - ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity); - if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, - "Sleep => pause with userLeaving=false"); - - startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */, - "sleep"); - shouldSleep = false ; - } else if (mPausingActivity != null) { - // Still waiting for something to pause; can't sleep yet. - ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity); - shouldSleep = false; - } - - if (!shuttingDown) { - if (containsActivityFromRootTask(mTaskSupervisor.mStoppingActivities)) { - // Still need to tell some activities to stop; can't sleep yet. - ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities", - mTaskSupervisor.mStoppingActivities.size()); - - mTaskSupervisor.scheduleIdle(); - shouldSleep = false; + final int[] sleepInProgress = {0}; + forAllLeafTasksAndLeafTaskFragments(taskFragment -> { + if (!taskFragment.sleepIfPossible(shuttingDown)) { + sleepInProgress[0]++; } - } - - if (shouldSleep) { - ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - !PRESERVE_WINDOWS); - } - - return shouldSleep; - } - - private boolean containsActivityFromRootTask(List<ActivityRecord> rs) { - for (ActivityRecord r : rs) { - if (r.getRootTask() == this) { - return true; - } - } - return false; - } - - final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) { - return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason); - } - - /** - * Start pausing the currently resumed activity. It is an error to call this if there - * is already an activity being paused or there is no resumed activity. - * - * @param userLeaving True if this should result in an onUserLeaving to the current activity. - * @param uiSleeping True if this is happening with the user interface going to sleep (the - * screen turning off). - * @param resuming The activity we are currently trying to resume or null if this is not being - * called as part of resuming the top activity, so we shouldn't try to instigate - * a resume here if not null. - * @param reason The reason of pausing the activity. - * @return Returns true if an activity now is in the PAUSING state, and we are waiting for - * it to tell us when it is done. - */ - final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, - ActivityRecord resuming, String reason) { - if (!isLeafTask()) { - final int[] pausing = {0}; - forAllLeafTasks((t) -> { - if (t.startPausingLocked(userLeaving, uiSleeping, resuming, reason)) { - pausing[0]++; - } - }, true /* traverseTopToBottom */); - return pausing[0] > 0; - } - - if (mPausingActivity != null) { - Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity - + " state=" + mPausingActivity.getState()); - if (!shouldSleepActivities()) { - // Avoid recursion among check for sleep and complete pause during sleeping. - // Because activity will be paused immediately after resume, just let pause - // be completed by the order of activity paused from clients. - completePauseLocked(false, resuming); - } - } - ActivityRecord prev = mResumedActivity; - - if (prev == null) { - if (resuming == null) { - Slog.wtf(TAG, "Trying to pause when nothing is resumed"); - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - return false; - } - - if (prev == resuming) { - Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed"); - return false; - } - - ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); - mPausingActivity = prev; - mLastPausedActivity = prev; - if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { - mTaskSupervisor.mNoHistoryActivities.add(prev); - } - prev.setState(PAUSING, "startPausingLocked"); - prev.getTask().touchActiveTime(); - - mAtmService.updateCpuStats(); - - boolean pauseImmediately = false; - boolean shouldAutoPip = false; - if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) { - // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous - // activity to be paused, while at the same time resuming the new resume activity - // only if the previous activity can't go into Pip since we want to give Pip - // activities a chance to enter Pip before resuming the next activity. - final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState( - "shouldResumeWhilePausing", userLeaving); - if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) { - shouldAutoPip = true; - } else if (!lastResumedCanPip) { - pauseImmediately = true; - } else { - // The previous activity may still enter PIP even though it did not allow auto-PIP. - } - } - - boolean didAutoPip = false; - if (prev.attachedToProcess()) { - if (shouldAutoPip) { - ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " - + "directly: %s", prev); - - didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs); - mPausingActivity = null; - } else { - ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev); - try { - EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), - prev.shortComponentName, "userLeaving=" + userLeaving, reason); - - mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), - prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, - prev.configChangeFlags, pauseImmediately)); - } catch (Exception e) { - // Ignore exception, if process died other code will cleanup. - Slog.w(TAG, "Exception thrown during pause", e); - mPausingActivity = null; - mLastPausedActivity = null; - mTaskSupervisor.mNoHistoryActivities.remove(prev); - } - } - } else { - mPausingActivity = null; - mLastPausedActivity = null; - mTaskSupervisor.mNoHistoryActivities.remove(prev); - } - - // If we are not going to sleep, we want to ensure the device is - // awake until the next activity is started. - if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) { - mTaskSupervisor.acquireLaunchWakelock(); - } - - // If already entered PIP mode, no need to keep pausing. - if (mPausingActivity != null && !didAutoPip) { - // Have the window manager pause its key dispatching until the new - // activity has started. If we're pausing the activity just because - // the screen is being turned off and the UI is sleeping, don't interrupt - // key dispatch; the same activity will pick it up again on wakeup. - if (!uiSleeping) { - prev.pauseKeyDispatchingLocked(); - } else { - ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off"); - } - - if (pauseImmediately) { - // If the caller said they don't want to wait for the pause, then complete - // the pause now. - completePauseLocked(false, resuming); - return false; - - } else { - prev.schedulePauseTimeout(); - return true; - } - - } else { - // This activity either failed to schedule the pause or it entered PIP mode, - // so just treat it as being paused now. - ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next."); - if (resuming == null) { - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - return false; - } - } - - @VisibleForTesting - void completePauseLocked(boolean resumeNext, ActivityRecord resuming) { - // Complete the pausing process of a pausing activity, so it doesn't make sense to - // operate on non-leaf tasks. - warnForNonLeafTask("completePauseLocked"); - - ActivityRecord prev = mPausingActivity; - ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev); - - if (prev != null) { - prev.setWillCloseOrEnterPip(false); - final boolean wasStopping = prev.isState(STOPPING); - prev.setState(PAUSED, "completePausedLocked"); - if (prev.finishing) { - // We will update the activity visibility later, no need to do in - // completeFinishing(). Updating visibility here might also making the next - // activities to be resumed, and could result in wrong app transition due to - // lack of previous activity information. - ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev); - prev = prev.completeFinishing(false /* updateVisibility */, - "completePausedLocked"); - } else if (prev.hasProcess()) { - ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s " - + "wasStopping=%b visibleRequested=%b", prev, wasStopping, - prev.mVisibleRequested); - if (prev.deferRelaunchUntilPaused) { - // Complete the deferred relaunch that was waiting for pause to complete. - ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev); - prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch); - } else if (wasStopping) { - // We are also stopping, the stop request must have gone soon after the pause. - // We can't clobber it, because the stop confirmation will not be handled. - // We don't need to schedule another stop, we only need to let it happen. - prev.setState(STOPPING, "completePausedLocked"); - } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) { - // Clear out any deferred client hide we might currently have. - prev.setDeferHidingClient(false); - // If we were visible then resumeTopActivities will release resources before - // stopping. - prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */, - "completePauseLocked"); - } - } else { - ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev); - prev = null; - } - // It is possible the activity was freezing the screen before it was paused. - // In that case go ahead and remove the freeze this activity has on the screen - // since it is no longer visible. - if (prev != null) { - prev.stopFreezingScreenLocked(true /*force*/); - } - mPausingActivity = null; - } - - if (resumeNext) { - final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); - if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) { - mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null); - } else { - checkReadyForSleep(); - final ActivityRecord top = - topRootTask != null ? topRootTask.topRunningActivity() : null; - if (top == null || (prev != null && top != prev)) { - // If there are no more activities available to run, do resume anyway to start - // something. Also if the top activity on the root task is not the just paused - // activity, we need to go ahead and resume it to ensure we complete an - // in-flight app switch. - mRootWindowContainer.resumeFocusedTasksTopActivities(); - } - } - } - - if (prev != null) { - prev.resumeKeyDispatchingLocked(); - } - - mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); - - // Notify when the task stack has changed, but only if visibilities changed (not just - // focus). Also if there is an active root pinned task - we always want to notify it about - // task stack changes, because its positioning may depend on it. - if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause - || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) { - mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged(); - mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false; - } + }, true /* traverseTopToBottom */); + return sleepInProgress[0] == 0; } boolean isTopRootTaskInDisplayArea() { @@ -5941,10 +4691,10 @@ class Task extends WindowContainer<WindowContainer> { * The activity is either starting or resuming. * Caller should ensure starting activity is visible. * @param preserveWindows Flag indicating whether windows should be preserved when updating - * configuration in {@link mEnsureActivitiesVisibleHelper}. + * configuration in {@link EnsureActivitiesVisibleHelper}. * @param configChanges Parts of the configuration that changed for this activity for evaluating * if the screen should be frozen as part of - * {@link mEnsureActivitiesVisibleHelper}. + * {@link EnsureActivitiesVisibleHelper}. * */ void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges, @@ -5960,21 +4710,22 @@ class Task extends WindowContainer<WindowContainer> { * The activity is either starting or resuming. * Caller should ensure starting activity is visible. * @param notifyClients Flag indicating whether the visibility updates should be sent to the - * clients in {@link mEnsureActivitiesVisibleHelper}. + * clients in {@link EnsureActivitiesVisibleHelper}. * @param preserveWindows Flag indicating whether windows should be preserved when updating - * configuration in {@link mEnsureActivitiesVisibleHelper}. + * configuration in {@link EnsureActivitiesVisibleHelper}. * @param configChanges Parts of the configuration that changed for this activity for evaluating * if the screen should be frozen as part of - * {@link mEnsureActivitiesVisibleHelper}. + * {@link EnsureActivitiesVisibleHelper}. */ // TODO: Should be re-worked based on the fact that each task as a root task in most cases. void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { mTaskSupervisor.beginActivityVisibilityUpdate(); try { - forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process( - starting, configChanges, preserveWindows, notifyClients), - true /* traverseTopToBottom */); + forAllLeafTasks(task -> { + task.updateActivityVisibilities(starting, configChanges, preserveWindows, + notifyClients); + }, true /* traverseTopToBottom */); // Notify WM shell that task visibilities may have changed forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false), @@ -6049,25 +4800,6 @@ class Task extends WindowContainer<WindowContainer> { } } - /** @see ActivityRecord#cancelInitializing() */ - void cancelInitializingActivities() { - // We don't want to clear starting window for activities that aren't behind fullscreen - // activities as we need to display their starting window until they are done initializing. - checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing); - } - - /** - * If an activity {@param toCheck} is given, this method returns {@code true} if the activity - * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling - * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded - * activities to the function. - */ - boolean checkBehindFullscreenActivity(ActivityRecord toCheck, - Consumer<ActivityRecord> handleBehindFullscreenActivity) { - return mCheckBehindFullscreenActivityHelper.process( - toCheck, handleBehindFullscreenActivity); - } - /** * Ensure that the top activity in the root task is resumed. * @@ -6108,7 +4840,8 @@ class Task extends WindowContainer<WindowContainer> { if (!child.isTopActivityFocusable()) { continue; } - if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) { + if (child.getVisibility(null /* starting */) + != TASK_FRAGMENT_VISIBILITY_VISIBLE) { break; } @@ -6155,383 +4888,25 @@ class Task extends WindowContainer<WindowContainer> { return false; } - // Find the next top-most activity to resume in this root task that is not finishing and is - // focusable. If it is not focusable, we will fall into the case below to resume the - // top activity in the next focusable task. - ActivityRecord next = topRunningActivity(true /* focusableOnly */); - - final boolean hasRunningActivity = next != null; - - // TODO: Maybe this entire condition can get removed? - if (hasRunningActivity && !isAttached()) { - return false; - } - mRootWindowContainer.cancelInitializingActivities(); - if (!hasRunningActivity) { - // There are no activities left in the root task, let's look somewhere else. + final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */); + if (topActivity == null) { + // There are no activities left in this task, let's look somewhere else. return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options); } - next.delayedResume = false; - final TaskDisplayArea taskDisplayArea = getDisplayArea(); - - // If the top activity is the resumed one, nothing to do. - if (mResumedActivity == next && next.isState(RESUMED) - && taskDisplayArea.allResumedActivitiesComplete()) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible - // we still want to check if the visibility of other windows have changed (e.g. bringing - // a fullscreen window forward to cover another freeform activity.) - if (taskDisplayArea.inMultiWindowMode()) { - taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */, true /* notifyClients */); - } - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity " - + "resumed %s", next); - return false; - } - - if (!next.canResumeByCompat()) { - return false; - } - - // If we are currently pausing an activity, then don't do anything until that is done. - final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete(); - if (!allPausedComplete) { - ProtoLog.v(WM_DEBUG_STATES, - "resumeTopActivityLocked: Skip resume: some activity pausing."); - - return false; - } - - // If we are sleeping, and there is no resumed activity, and the top activity is paused, - // well that is the state we want. - if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) { - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Going to sleep and" - + " all paused"); - return false; - } - - // Make sure that the user who owns this activity is started. If not, - // we will just leave it as is because someone should be bringing - // another user's activities to the top of the stack. - if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) { - Slog.w(TAG, "Skipping resume of top activity " + next - + ": user " + next.mUserId + " is stopped"); - return false; - } - - // The activity may be waiting for stop, but that is no longer - // appropriate for it. - mTaskSupervisor.mStoppingActivities.remove(next); - - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); - - mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); - - ActivityRecord lastResumed = null; - final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask(); - if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) { - // So, why aren't we using prev here??? See the param comment on the method. prev - // doesn't represent the last resumed activity. However, the last focus stack does if - // it isn't null. - lastResumed = lastFocusedRootTask.getResumedActivity(); - } - - boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next); - if (mResumedActivity != null) { - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity); - pausing |= startPausingLocked(false /* uiSleeping */, next, - "resumeTopActivityInnerLocked"); - } - if (pausing) { - ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivityLocked: Skip resume: need to" - + " start pausing"); - // At this point we want to put the upcoming activity's process - // at the top of the LRU list, since we know we will be needing it - // very soon and it would be a waste to let it get killed if it - // happens to be sitting towards the end. - if (next.attachedToProcess()) { - next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, - true /* activityChange */, false /* updateOomAdj */, - false /* addPendingTopUid */); - } else if (!next.isProcessRunning()) { - // Since the start-process is asynchronous, if we already know the process of next - // activity isn't running, we can start the process earlier to save the time to wait - // for the current activity to be paused. - final boolean isTop = this == taskDisplayArea.getFocusedRootTask(); - mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop, - isTop ? "pre-top-activity" : "pre-activity"); - } - if (lastResumed != null) { - lastResumed.setWillCloseOrEnterPip(true); - } - return true; - } else if (mResumedActivity == next && next.isState(RESUMED) - && taskDisplayArea.allResumedActivitiesComplete()) { - // It is possible for the activity to be resumed when we paused back stacks above if the - // next activity doesn't have to wait for pause to complete. - // So, nothing else to-do except: - // Make sure we have executed any pending transitions, since there - // should be nothing left to do at this point. - executeAppTransition(options); - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity resumed " - + "(dontWaitForPause) %s", next); - return true; - } - - // If the most recent activity was noHistory but was only stopped rather - // than stopped+finished because the device went to sleep, we need to make - // sure to finish it as we're making a new activity topmost. - if (shouldSleepActivities()) { - mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next); - } - - if (prev != null && prev != next && next.nowVisible) { - - // The next activity is already visible, so hide the previous - // activity's windows right now so we can show the new one ASAP. - // We only do this if the previous is finishing, which should mean - // it is on top of the one being resumed so hiding it quickly - // is good. Otherwise, we want to do the normal route of allowing - // the resumed activity to be shown so we can decide if the - // previous should actually be hidden depending on whether the - // new one is found to be full-screen or not. - if (prev.finishing) { - prev.setVisibility(false); - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, - "Not waiting for visible to hide: " + prev - + ", nowVisible=" + next.nowVisible); - } else { - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, - "Previous already visible but still waiting to hide: " + prev - + ", nowVisible=" + next.nowVisible); - } - - } - - // Launching this app's activity, make sure the app is no longer - // considered stopped. - try { - mTaskSupervisor.getActivityMetricsLogger() - .notifyBeforePackageUnstopped(next.packageName); - mAtmService.getPackageManager().setPackageStoppedState( - next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */ - } catch (RemoteException e1) { - } catch (IllegalArgumentException e) { - Slog.w(TAG, "Failed trying to unstop package " - + next.packageName + ": " + e); - } - - // We are starting up the next activity, so tell the window manager - // that the previous one will be hidden soon. This way it can know - // to ignore it when computing the desired screen orientation. - boolean anim = true; - final DisplayContent dc = taskDisplayArea.mDisplayContent; - if (prev != null) { - if (prev.finishing) { - if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, - "Prepare close transition: prev=" + prev); - if (mTaskSupervisor.mNoAnimActivities.contains(prev)) { - anim = false; - dc.prepareAppTransition(TRANSIT_NONE); - } else { - dc.prepareAppTransition(TRANSIT_CLOSE); - } - prev.setVisibility(false); - } else { - if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, - "Prepare open transition: prev=" + prev); - if (mTaskSupervisor.mNoAnimActivities.contains(next)) { - anim = false; - dc.prepareAppTransition(TRANSIT_NONE); - } else { - dc.prepareAppTransition(TRANSIT_OPEN, - next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); - } - } - } else { - if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); - if (mTaskSupervisor.mNoAnimActivities.contains(next)) { - anim = false; - dc.prepareAppTransition(TRANSIT_NONE); - } else { - dc.prepareAppTransition(TRANSIT_OPEN); - } - } - - if (anim) { - next.applyOptionsAnimation(); - } else { - next.abortAndClearOptionsAnimation(); - } - - mTaskSupervisor.mNoAnimActivities.clear(); - - if (next.attachedToProcess()) { - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next - + " stopped=" + next.stopped - + " visibleRequested=" + next.mVisibleRequested); - - // If the previous activity is translucent, force a visibility update of - // the next activity, so that it's added to WM's opening app list, and - // transition animation can be set up properly. - // For example, pressing Home button with a translucent activity in focus. - // Launcher is already visible in this case. If we don't add it to opening - // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a - // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation. - final boolean lastActivityTranslucent = lastFocusedRootTask != null - && (lastFocusedRootTask.inMultiWindowMode() - || (lastFocusedRootTask.mLastPausedActivity != null - && !lastFocusedRootTask.mLastPausedActivity.occludesParent())); - - // This activity is now becoming visible. - if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) { - next.setVisibility(true); - } - - // schedule launch ticks to collect information about slow apps. - next.startLaunchTickingLocked(); - - ActivityRecord lastResumedActivity = - lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity(); - final ActivityState lastState = next.getState(); - - mAtmService.updateCpuStats(); - - ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next); - - next.setState(RESUMED, "resumeTopActivityInnerLocked"); - - // Have the window manager re-evaluate the orientation of - // the screen based on the new activity order. - boolean notUpdated = true; - - // Activity should also be visible if set mLaunchTaskBehind to true (see - // ActivityRecord#shouldBeVisibleIgnoringKeyguard()). - if (shouldBeVisible(next)) { - // We have special rotation behavior when here is some active activity that - // requests specific orientation or Keyguard is locked. Make sure all activity - // visibilities are set correctly as well as the transition is updated if needed - // to get the correct rotation behavior. Otherwise the following call to update - // the orientation may cause incorrect configurations delivered to client as a - // result of invisible window resize. - // TODO: Remove this once visibilities are set correctly immediately when - // starting an activity. - notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(), - true /* markFrozenIfConfigChanged */, false /* deferResume */); - } - - if (notUpdated) { - // The configuration update wasn't able to keep the existing - // instance of the activity, and instead started a new one. - // We should be all done, but let's just make sure our activity - // is still at the top and schedule another run if something - // weird happened. - ActivityRecord nextNext = topRunningActivity(); - ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: " - + "%s, new next: %s", next, nextNext); - if (nextNext != next) { - // Do over! - mTaskSupervisor.scheduleResumeTopActivities(); - } - if (!next.mVisibleRequested || next.stopped) { - next.setVisibility(true); - } - next.completeResumeLocked(); - return true; - } - - try { - final ClientTransaction transaction = - ClientTransaction.obtain(next.app.getThread(), next.appToken); - // Deliver all pending results. - ArrayList<ResultInfo> a = next.results; - if (a != null) { - final int N = a.size(); - if (!next.finishing && N > 0) { - if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, - "Delivering results to " + next + ": " + a); - transaction.addCallback(ActivityResultItem.obtain(a)); - } - } - - if (next.newIntents != null) { - transaction.addCallback( - NewIntentItem.obtain(next.newIntents, true /* resume */)); - } - - // Well the app will no longer be stopped. - // Clear app token stopped state in window manager if needed. - next.notifyAppResumed(next.stopped); - - EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next), - next.getTask().mTaskId, next.shortComponentName); - - mAtmService.getAppWarningsLocked().onResumeActivity(next); - next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState); - next.abortAndClearOptionsAnimation(); - transaction.setLifecycleStateRequest( - ResumeActivityItem.obtain(next.app.getReportedProcState(), - dc.isNextTransitionForward())); - mAtmService.getLifecycleManager().scheduleTransaction(transaction); - - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next); - } catch (Exception e) { - // Whoops, need to restart this activity! - ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: " - + "%s", lastState, next); - next.setState(lastState, "resumeTopActivityInnerLocked"); - - // lastResumedActivity being non-null implies there is a lastStack present. - if (lastResumedActivity != null) { - lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked"); - } - - Slog.i(TAG, "Restarting because process died: " + next); - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null - && lastFocusedRootTask.isTopRootTaskInDisplayArea()) { - next.showStartingWindow(false /* taskSwitch */); - } - mTaskSupervisor.startSpecificActivity(next, true, false); - return true; - } - - // From this point on, if something goes wrong there is no way - // to recover the activity. - try { - next.completeResumeLocked(); - } catch (Exception e) { - // If any exception gets thrown, toss away this - // activity and try the next one. - Slog.w(TAG, "Exception thrown during resume of " + next, e); - next.finishIfPossible("resume-exception", true /* oomAdj */); - return true; - } - } else { - // Whoops, need to restart this activity! - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else { - if (SHOW_APP_STARTING_PREVIEW) { - next.showStartingWindow(false /* taskSwich */); - } - if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); + final boolean[] resumed = new boolean[1]; + final TaskFragment topFragment = topActivity.getTaskFragment(); + resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause); + forAllLeafTaskFragments(f -> { + if (topFragment == f) { + return; } - ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Restarting %s", next); - mTaskSupervisor.startSpecificActivity(next, true, true); - } - return true; + resumed[0] |= f.resumeTopActivity(prev, options, deferPause); + }, true); + return resumed[0]; } /** @@ -7212,13 +5587,6 @@ class Task extends WindowContainer<WindowContainer> { return true; } - /** - * Ensures all visible activities at or below the input activity have the right configuration. - */ - void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) { - mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow); - } - // TODO: Can only be called from special methods in ActivityTaskSupervisor. // Need to consolidate those calls points into this resize method so anyone can call directly. void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) { @@ -7272,114 +5640,35 @@ class Task extends WindowContainer<WindowContainer> { } } - /** - * Reset local parameters because an app's activity died. - * @param app The app of the activity that died. - * @return {@code true} if the process of the pausing activity is died. - */ - boolean handleAppDied(WindowProcessController app) { - warnForNonLeafTask("handleAppDied"); - boolean isPausingDied = false; - if (mPausingActivity != null && mPausingActivity.app == app) { - ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s", - mPausingActivity); - mPausingActivity = null; - isPausingDied = true; - } - if (mLastPausedActivity != null && mLastPausedActivity.app == app) { - if (mLastPausedActivity.isNoHistory()) { - mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity); - } - mLastPausedActivity = null; - } - return isPausingDied; - } - boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, String dumpPackage, final boolean needSep) { - Runnable headerPrinter = () -> { - if (needSep) { - pw.println(); - } - pw.println(" RootTask #" + getRootTaskId() - + ": type=" + activityTypeToString(getActivityType()) - + " mode=" + windowingModeToString(getWindowingMode())); - pw.println(" isSleeping=" + shouldSleepActivities()); - pw.println(" mBounds=" + getRequestedOverrideBounds()); - pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer); - }; - - boolean printed = false; + return dump(" ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */); + } - if (dumpPackage == null) { - // If we are not filtering by package, we want to print absolutely everything, - // so always print the header even if there are no tasks/activities inside. - headerPrinter.run(); - headerPrinter = null; - printed = true; + @Override + void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) { + pw.print(prefix); pw.print("* "); pw.println(this); + pw.println(prefix + " mBounds=" + getRequestedOverrideBounds()); + pw.println(prefix + " mCreatedByOrganizer=" + mCreatedByOrganizer); + if (mLastNonFullscreenBounds != null) { + pw.print(prefix); pw.print(" mLastNonFullscreenBounds="); + pw.println(mLastNonFullscreenBounds); } - - printed |= printThisActivity(pw, getPausingActivity(), dumpPackage, false, - " mPausingActivity: ", null); - printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false, - " mResumedActivity: ", null); if (dumpAll) { - printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false, - " mLastPausedActivity: ", null); + printThisActivity(pw, mLastPausedActivity, dumpPackage, false, + prefix + " mLastPausedActivity: ", null); } - - printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter); - - return printed; - } - - private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, - boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) { - if (!hasChild()) { - return false; + if (isLeafTask()) { + pw.println(prefix + " isSleeping=" + shouldSleepActivities()); + printThisActivity(pw, getTopPausingActivity(), dumpPackage, false, + prefix + " topPausingActivity=", null); + printThisActivity(pw, getTopResumedActivity(), dumpPackage, false, + prefix + " topResumedActivity=", null); + if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) { + pw.print(prefix); pw.print(" mMinWidth="); pw.print(mMinWidth); + pw.print(" mMinHeight="); pw.println(mMinHeight); + } } - final AtomicBoolean printedHeader = new AtomicBoolean(false); - final AtomicBoolean printed = new AtomicBoolean(false); - forAllLeafTasks((task) -> { - final String prefix = " "; - Runnable headerPrinter = () -> { - printed.set(true); - if (!printedHeader.get()) { - if (needSep) { - pw.println(""); - } - if (header != null) { - header.run(); - } - printedHeader.set(true); - } - pw.print(prefix); pw.print("* "); pw.println(task); - pw.print(prefix); pw.print(" mBounds="); - pw.println(task.getRequestedOverrideBounds()); - pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth); - pw.print(" mMinHeight="); pw.println(task.mMinHeight); - if (mLastNonFullscreenBounds != null) { - pw.print(prefix); - pw.print(" mLastNonFullscreenBounds="); - pw.println(task.mLastNonFullscreenBounds); - } - task.dump(pw, prefix + " "); - }; - if (dumpPackage == null) { - // If we are not filtering by package, we want to print absolutely everything, - // so always print the header even if there are no activities inside. - headerPrinter.run(); - headerPrinter = null; - } - final ArrayList<ActivityRecord> activities = new ArrayList<>(); - // Add activities by traversing the hierarchy from bottom to top, since activities - // are dumped in reverse order in {@link ActivityTaskSupervisor#dumpHistoryList()}. - task.forAllActivities((Consumer<ActivityRecord>) activities::add, - false /* traverseTopToBottom */); - dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient, - dumpPackage, false, headerPrinter, task); - }, true /* traverseTopToBottom */); - return printed.get(); } ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) { @@ -7779,6 +6068,7 @@ class Task extends WindowContainer<WindowContainer> { return mAnimatingActivityRegistry; } + @Override void executeAppTransition(ActivityOptions options) { mDisplayContent.executeAppTransition(); ActivityOptions.abort(options); @@ -7801,10 +6091,6 @@ class Task extends WindowContainer<WindowContainer> { return display != null ? display.isSleeping() : mAtmService.isSleepingLocked(); } - boolean shouldSleepOrShutDownActivities() { - return shouldSleepActivities() || mAtmService.mShuttingDown; - } - private Rect getRawBounds() { return super.getBounds(); } @@ -7823,14 +6109,12 @@ class Task extends WindowContainer<WindowContainer> { } final long token = proto.start(fieldId); - super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); proto.write(TaskProto.ID, mTaskId); - proto.write(DISPLAY_ID, getDisplayId()); proto.write(ROOT_TASK_ID, getRootTaskId()); - if (mResumedActivity != null) { - mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); + if (getTopResumedActivity() != null) { + getTopResumedActivity().writeIdentifierToProto(proto, RESUMED_ACTIVITY); } if (realActivity != null) { proto.write(REAL_ACTIVITY, realActivity.flattenToShortString()); @@ -7838,11 +6122,7 @@ class Task extends WindowContainer<WindowContainer> { if (origActivity != null) { proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString()); } - proto.write(ACTIVITY_TYPE, getActivityType()); proto.write(RESIZE_MODE, mResizeMode); - proto.write(MIN_WIDTH, mMinWidth); - proto.write(MIN_HEIGHT, mMinHeight); - proto.write(FILLS_PARENT, matchParentBounds()); getRawBounds().dumpDebug(proto, BOUNDS); @@ -7859,6 +6139,8 @@ class Task extends WindowContainer<WindowContainer> { proto.write(AFFINITY, affinity); proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null); + super.dumpDebug(proto, TASK_FRAGMENT, logLevel); + proto.end(token); } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index d450dbffe4a1..10c16ff96a16 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -34,11 +34,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK; import static com.android.server.wm.DisplayContent.alwaysCreateRootTask; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -469,7 +468,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Update the top resumed activity because the preferred top focusable task may be changed. mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded(); - final ActivityRecord r = child.getResumedActivity(); + final ActivityRecord r = child.getTopResumedActivity(); if (r != null && r == mRootWindowContainer.getTopResumedActivity()) { mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt"); } @@ -1231,7 +1230,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { + adjacentFlagRootTask); } - if (adjacentFlagRootTask.mAdjacentTask == null) { + if (adjacentFlagRootTask.getAdjacentTaskFragment() == null) { throw new UnsupportedOperationException( "Can't set non-adjacent root as launch adjacent flag root tr=" + adjacentFlagRootTask); @@ -1269,8 +1268,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // If the adjacent launch is coming from the same root, launch to adjacent root instead. if (sourceTask != null && sourceTask.getRootTask().mTaskId == mLaunchAdjacentFlagRootTask.mTaskId - && mLaunchAdjacentFlagRootTask.mAdjacentTask != null) { - return mLaunchAdjacentFlagRootTask.mAdjacentTask; + && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null) { + return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask(); } else { return mLaunchAdjacentFlagRootTask; } @@ -1280,8 +1279,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) { final Task launchRootTask = mLaunchRootTasks.get(i).task; // Return the focusable root task for improving the UX with staged split screen. - final Task adjacentRootTask = launchRootTask != null - ? launchRootTask.mAdjacentTask : null; + final TaskFragment adjacentTaskFragment = launchRootTask != null + ? launchRootTask.getAdjacentTaskFragment() : null; + final Task adjacentRootTask = + adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null; if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) { return adjacentRootTask; } else { @@ -1373,11 +1374,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } // TODO(b/111541062): Move this into Task#getResumedActivity() // Check if the focused root task has the resumed activity - ActivityRecord resumedActivity = focusedRootTask.getResumedActivity(); + ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity(); if (resumedActivity == null || resumedActivity.app == null) { // If there is no registered resumed activity in the root task or it is not running - // try to use previously resumed one. - resumedActivity = focusedRootTask.getPausingActivity(); + resumedActivity = focusedRootTask.getTopPausingActivity(); if (resumedActivity == null || resumedActivity.app == null) { // If previously resumed activity doesn't work either - find the topmost running // activity that can be focused. @@ -1404,7 +1405,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Clear last paused activity if focused root task changed while sleeping, so that the // top activity of current focused task can be resumed. if (mDisplayContent.isSleeping()) { - currentFocusedTask.mLastPausedActivity = null; + currentFocusedTask.clearLastPausedActivity(); } mLastFocusedRootTask = prevFocusedTask; @@ -1425,7 +1426,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { continue; } - final ActivityRecord r = mChildren.get(i).asTask().getResumedActivity(); + final ActivityRecord r = mChildren.get(i).asTask().getTopResumedActivity(); if (r != null && !r.isState(RESUMED)) { return false; } @@ -1451,18 +1452,30 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { */ boolean pauseBackTasks(ActivityRecord resuming) { final int[] someActivityPaused = {0}; - forAllLeafTasks((task) -> { - final ActivityRecord resumedActivity = task.getResumedActivity(); - if (resumedActivity != null - && (task.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE - || !task.isTopActivityFocusable())) { - ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s " - + "mResumedActivity=%s", task, resumedActivity); - if (task.startPausingLocked(false /* uiSleeping*/, - resuming, "pauseBackTasks")) { - someActivityPaused[0]++; + forAllLeafTasks(leafTask -> { + // Check if the direct child resumed activity in the leaf task needed to be paused if + // the leaf task is not a leaf task fragment. + if (!leafTask.isLeafTaskFragment()) { + final ActivityRecord top = topRunningActivity(); + final ActivityRecord resumedActivity = leafTask.getResumedActivity(); + if (resumedActivity != null && top.getTaskFragment() != leafTask) { + // Pausing the resumed activity because it is occluded by other task fragment. + if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) { + someActivityPaused[0]++; + } } } + + leafTask.forAllLeafTaskFragments((taskFrag) -> { + final ActivityRecord resumedActivity = taskFrag.getResumedActivity(); + if (resumedActivity != null + && (taskFrag.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE + || !taskFrag.isTopActivityFocusable())) { + if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) { + someActivityPaused[0]++; + } + } + }, true /* traverseTopToBottom */); }, true /* traverseTopToBottom */); return someActivityPaused[0] > 0; } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java new file mode 100644 index 000000000000..b263909d7e6d --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -0,0 +1,2109 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static android.os.UserHandle.USER_NULL; +import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OPEN; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; +import static com.android.server.wm.IdentifierProto.HASH_CODE; +import static com.android.server.wm.IdentifierProto.TITLE; +import static com.android.server.wm.IdentifierProto.USER_ID; +import static com.android.server.wm.TaskFragmentProto.ACTIVITY_TYPE; +import static com.android.server.wm.TaskFragmentProto.DISPLAY_ID; +import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT; +import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH; +import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.app.ResultInfo; +import android.app.WindowConfiguration; +import android.app.servertransaction.ActivityResultItem; +import android.app.servertransaction.ClientTransaction; +import android.app.servertransaction.NewIntentItem; +import android.app.servertransaction.PauseActivityItem; +import android.app.servertransaction.ResumeActivityItem; +import android.content.ComponentName; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import android.view.DisplayInfo; +import android.window.ITaskFragmentOrganizer; +import android.window.TaskFragmentInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.function.pooled.PooledFunction; +import com.android.internal.util.function.pooled.PooledLambda; +import com.android.internal.util.function.pooled.PooledPredicate; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * A basic container that can be used to contain activities or other {@link TaskFragment}, which + * also able to manage the activity lifecycle and updates the visibilities of the activities in it. + */ +class TaskFragment extends WindowContainer<WindowContainer> { + @IntDef(prefix = {"TASK_FRAGMENT_VISIBILITY"}, value = { + TASK_FRAGMENT_VISIBILITY_VISIBLE, + TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + TASK_FRAGMENT_VISIBILITY_INVISIBLE, + }) + @interface TaskFragmentVisibility {} + + /** + * TaskFragment is visible. No other TaskFragment(s) on top that fully or partially occlude it. + */ + static final int TASK_FRAGMENT_VISIBILITY_VISIBLE = 0; + + /** TaskFragment is partially occluded by other translucent TaskFragment(s) on top of it. */ + static final int TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1; + + /** TaskFragment is completely invisible. */ + static final int TASK_FRAGMENT_VISIBILITY_INVISIBLE = 2; + + private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskFragment" : TAG_ATM; + private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; + private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; + private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION; + + /** Set to false to disable the preview that is shown while a new activity is being started. */ + static final boolean SHOW_APP_STARTING_PREVIEW = true; + + /** + * Indicate that the minimal width/height should use the default value. + * + * @see #mMinWidth + * @see #mMinHeight + */ + static final int INVALID_MIN_SIZE = -1; + + final ActivityTaskManagerService mAtmService; + final ActivityTaskSupervisor mTaskSupervisor; + final RootWindowContainer mRootWindowContainer; + private final TaskFragmentOrganizerController mTaskFragmentOrganizerController; + + /** + * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it + * should use the default minimal width. + */ + int mMinWidth; + + /** + * Minimal height of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it + * should use the default minimal height. + */ + int mMinHeight; + + // The TaskFragment that adjacent to this one. + private TaskFragment mAdjacentTaskFragment; + + /** + * When we are in the process of pausing an activity, before starting the + * next one, this variable holds the activity that is currently being paused. + * + * Only set at leaf task fragments. + */ + @Nullable + private ActivityRecord mPausingActivity = null; + + /** + * This is the last activity that we put into the paused state. This is + * used to determine if we need to do an activity transition while sleeping, + * when we normally hold the top activity paused. + */ + ActivityRecord mLastPausedActivity = null; + + /** + * Current activity that is resumed, or null if there is none. + * Only set at leaf task fragments. + */ + @Nullable + private ActivityRecord mResumedActivity = null; + + /** + * This TaskFragment was created by an organizer which has the following implementations. + * <ul> + * <li>The TaskFragment won't be removed when it is empty. Removal has to be an explicit + * request from the organizer.</li> + * <li>If this fragment is a Task object then unlike other non-root tasks, it's direct + * children are visible to the organizer for ordering purposes.</li> + * <li>A TaskFragment can be created by {@link android.window.TaskFragmentOrganizer}, and + * a Task can be created by {@link android.window.TaskOrganizer}.</li> + * </ul> + */ + @VisibleForTesting + boolean mCreatedByOrganizer; + + /** Organizer that organizing this TaskFragment. */ + // TODO(b/190433129) set the value when creating TaskFragment from WCT. + @Nullable + private ITaskFragmentOrganizer mTaskFragmentOrganizer; + + /** Client assigned unique token for this TaskFragment if this is created by an organizer. */ + // TODO(b/190433129) set the value when creating TaskFragment from WCT. + @Nullable + private IBinder mFragmentToken; + + /** + * The component name of the root activity that initiated this TaskFragment, which will be used + * to configure the relationships for TaskFragments. + */ + // TODO(b/190433129) set the value when creating TaskFragment from WCT. + @Nullable + private ComponentName mInitialComponentName; + + private final Rect mTmpInsets = new Rect(); + private final Rect mTmpBounds = new Rect(); + private final Rect mTmpFullBounds = new Rect(); + private final Rect mTmpStableBounds = new Rect(); + private final Rect mTmpNonDecorBounds = new Rect(); + + private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper = + new EnsureActivitiesVisibleHelper(this); + private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper = + new EnsureVisibleActivitiesConfigHelper(); + private class EnsureVisibleActivitiesConfigHelper { + private boolean mUpdateConfig; + private boolean mPreserveWindow; + private boolean mBehindFullscreen; + + void reset(boolean preserveWindow) { + mPreserveWindow = preserveWindow; + mUpdateConfig = false; + mBehindFullscreen = false; + } + + void process(ActivityRecord start, boolean preserveWindow) { + if (start == null || !start.mVisibleRequested) { + return; + } + reset(preserveWindow); + + final PooledFunction f = PooledLambda.obtainFunction( + EnsureVisibleActivitiesConfigHelper::processActivity, this, + PooledLambda.__(ActivityRecord.class)); + forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/); + f.recycle(); + + if (mUpdateConfig) { + // Ensure the resumed state of the focus activity if we updated the configuration of + // any activity. + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + } + + boolean processActivity(ActivityRecord r) { + mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow); + mBehindFullscreen |= r.occludesParent(); + return mBehindFullscreen; + } + } + + TaskFragment(ActivityTaskManagerService atmService, boolean createdByOrganizer) { + super(atmService.mWindowManager); + + mAtmService = atmService; + mTaskSupervisor = atmService.mTaskSupervisor; + mRootWindowContainer = mAtmService.mRootWindowContainer; + mCreatedByOrganizer = createdByOrganizer; + mTaskFragmentOrganizerController = + mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController; + } + + void setAdjacentTaskFragment(TaskFragment taskFragment) { + mAdjacentTaskFragment = taskFragment; + taskFragment.mAdjacentTaskFragment = this; + } + + TaskFragment getAdjacentTaskFragment() { + return mAdjacentTaskFragment; + } + + /** @return the currently resumed activity. */ + ActivityRecord getResumedActivity() { + return mResumedActivity; + } + + void setResumedActivity(ActivityRecord r, String reason) { + warnForNonLeafTaskFragment("setResumedActivity"); + if (mResumedActivity == r) { + return; + } + + if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { + Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: " + + mResumedActivity + " to:" + r + " reason:" + reason); + } + mResumedActivity = r; + mTaskSupervisor.updateTopResumedActivityIfNeeded(); + } + + @VisibleForTesting + void setPausingActivity(ActivityRecord pausing) { + mPausingActivity = pausing; + } + + ActivityRecord getPausingActivity() { + return mPausingActivity; + } + + int getDisplayId() { + final DisplayContent dc = getDisplayContent(); + return dc != null ? dc.mDisplayId : INVALID_DISPLAY; + } + + @Nullable + Task getTask() { + if (asTask() != null) { + return asTask(); + } + + TaskFragment parent = getParent() != null ? getParent().asTaskFragment() : null; + return parent != null ? parent.getTask() : null; + } + + @Override + @Nullable + TaskDisplayArea getDisplayArea() { + return (TaskDisplayArea) super.getDisplayArea(); + } + + @Override + public boolean isAttached() { + final TaskDisplayArea taskDisplayArea = getDisplayArea(); + return taskDisplayArea != null && !taskDisplayArea.isRemoved(); + } + + /** + * Returns the root {@link TaskFragment}, which is usually also a {@link Task}. + */ + @NonNull + TaskFragment getRootTaskFragment() { + final WindowContainer parent = getParent(); + if (parent == null) return this; + + final TaskFragment parentTaskFragment = parent.asTaskFragment(); + return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment(); + } + + @Override + TaskFragment asTaskFragment() { + return this; + } + + + /** + * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}. + */ + private void warnForNonLeafTaskFragment(String func) { + if (!isLeafTaskFragment()) { + Slog.w(TAG, func + " on non-leaf task fragment " + this); + } + } + + boolean hasDirectChildActivities() { + for (int i = mChildren.size() - 1; i >= 0; --i) { + if (mChildren.get(i).asActivityRecord() != null) { + return true; + } + } + return false; + } + + void cleanUpActivityReferences(@NonNull ActivityRecord r) { + if (mPausingActivity != null && mPausingActivity == r) { + mPausingActivity = null; + } + + if (mResumedActivity != null && mResumedActivity == r) { + setResumedActivity(null, "cleanUpActivityReferences"); + } + r.removeTimeouts(); + } + + /** + * Returns whether this TaskFragment is currently forced to be hidden for any reason. + */ + protected boolean isForceHidden() { + return false; + } + + boolean isLeafTaskFragment() { + for (int i = mChildren.size() - 1; i >= 0; --i) { + if (mChildren.get(i).asTaskFragment() != null) { + return false; + } + } + return true; + } + + /** + * This should be called when an child activity changes state. This should only + * be called from + * {@link ActivityRecord#setState(ActivityRecord.State, String)} . + * @param record The {@link ActivityRecord} whose state has changed. + * @param state The new state. + * @param reason The reason for the change. + */ + void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state, + String reason) { + warnForNonLeafTaskFragment("onActivityStateChanged"); + if (record == mResumedActivity && state != RESUMED) { + setResumedActivity(null, reason + " - onActivityStateChanged"); + } + + if (state == RESUMED) { + if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { + Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason); + } + setResumedActivity(record, reason + " - onActivityStateChanged"); + if (record == mRootWindowContainer.getTopResumedActivity()) { + mAtmService.setResumedActivityUncheckLocked(record, reason); + } + mTaskSupervisor.mRecentTasks.add(record.getTask()); + } + } + + /** + * Resets local parameters because an app's activity died. + * @param app The app of the activity that died. + * @return {@code true} if the process of the pausing activity is died. + */ + boolean handleAppDied(WindowProcessController app) { + warnForNonLeafTaskFragment("handleAppDied"); + boolean isPausingDied = false; + if (mPausingActivity != null && mPausingActivity.app == app) { + ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s", + mPausingActivity); + mPausingActivity = null; + isPausingDied = true; + } + if (mLastPausedActivity != null && mLastPausedActivity.app == app) { + if (mLastPausedActivity.isNoHistory()) { + mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity); + } + mLastPausedActivity = null; + } + return isPausingDied; + } + + void awakeFromSleeping() { + if (mPausingActivity != null) { + Slog.d(TAG, "awakeFromSleeping: previously pausing activity didn't pause"); + mPausingActivity.activityPaused(true); + } + } + + /** + * Tries to put the activities in the task fragment to sleep. + * + * If the task fragment is not in a state where its activities can be put to sleep, this + * function will start any necessary actions to move the task fragment into such a state. + * It is expected that this function get called again when those actions complete. + * + * @param shuttingDown {@code true} when the called because the device is shutting down. + * @return true if the root task finished going to sleep, false if the root task only started + * the process of going to sleep (checkReadyForSleep will be called when that process finishes). + */ + boolean sleepIfPossible(boolean shuttingDown) { + boolean shouldSleep = true; + if (mResumedActivity != null) { + // Still have something resumed; can't sleep until it is paused. + ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity); + startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */, + "sleep"); + shouldSleep = false; + } else if (mPausingActivity != null) { + // Still waiting for something to pause; can't sleep yet. + ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity); + shouldSleep = false; + } + + if (!shuttingDown) { + if (containsStoppingActivity()) { + // Still need to tell some activities to stop; can't sleep yet. + ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities", + mTaskSupervisor.mStoppingActivities.size()); + + mTaskSupervisor.scheduleIdle(); + shouldSleep = false; + } + } + + if (shouldSleep) { + updateActivityVisibilities(null /* starting */, 0 /* configChanges */, + !PRESERVE_WINDOWS, true /* notifyClients */); + } + + return shouldSleep; + } + + private boolean containsStoppingActivity() { + for (int i = mTaskSupervisor.mStoppingActivities.size() - 1; i >= 0; --i) { + ActivityRecord r = mTaskSupervisor.mStoppingActivities.get(i); + if (r.getTaskFragment() == this) { + return true; + } + } + return false; + } + + /** + * Returns true if the TaskFragment is translucent and can have other contents visible behind + * it if needed. A TaskFragment is considered translucent if it don't contain a visible or + * starting (about to be visible) activity that is fullscreen (opaque). + * @param starting The currently starting activity or null if there is none. + */ + @VisibleForTesting + boolean isTranslucent(ActivityRecord starting) { + if (!isAttached() || isForceHidden()) { + return true; + } + final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity, + PooledLambda.__(ActivityRecord.class), starting); + final ActivityRecord opaque = getActivity(p); + p.recycle(); + return opaque == null; + } + + private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) { + if (r.finishing) { + // We don't factor in finishing activities when determining translucency since + // they will be gone soon. + return false; + } + + if (!r.visibleIgnoringKeyguard && r != starting) { + // Also ignore invisible activities that are not the currently starting + // activity (about to be visible). + return false; + } + + if (r.occludesParent()) { + // Root task isn't translucent if it has at least one fullscreen activity + // that is visible. + return true; + } + return false; + } + + ActivityRecord topRunningActivity() { + return topRunningActivity(false /* focusableOnly */); + } + + ActivityRecord topRunningActivity(boolean focusableOnly) { + // Split into 2 to avoid object creation due to variable capture. + if (focusableOnly) { + return getActivity((r) -> r.canBeTopRunning() && r.isFocusable()); + } else { + return getActivity(ActivityRecord::canBeTopRunning); + } + } + + boolean isTopActivityFocusable() { + final ActivityRecord r = topRunningActivity(); + return r != null ? r.isFocusable() + : (isFocusable() && getWindowConfiguration().canReceiveKeys()); + } + + /** + * Returns the visibility state of this TaskFragment. + * + * @param starting The currently starting activity or null if there is none. + */ + @TaskFragmentVisibility + int getVisibility(ActivityRecord starting) { + if (!isAttached() || isForceHidden()) { + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + + if (isTopActivityLaunchedBehind()) { + return TASK_FRAGMENT_VISIBILITY_VISIBLE; + } + + boolean gotRootSplitScreenFragment = false; + boolean gotOpaqueSplitScreenPrimary = false; + boolean gotOpaqueSplitScreenSecondary = false; + boolean gotTranslucentFullscreen = false; + boolean gotTranslucentSplitScreenPrimary = false; + boolean gotTranslucentSplitScreenSecondary = false; + boolean shouldBeVisible = true; + + // This TaskFragment is only considered visible if all its parent TaskFragments are + // considered visible, so check the visibility of all ancestor TaskFragment first. + final WindowContainer parent = getParent(); + if (parent.asTaskFragment() != null) { + final int parentVisibility = parent.asTaskFragment().getVisibility(starting); + if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) { + // Can't be visible if parent isn't visible + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } else if (parentVisibility == TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) { + // Parent is behind a translucent container so the highest visibility this container + // can get is that. + gotTranslucentFullscreen = true; + } + } + + final List<TaskFragment> adjacentTaskFragments = new ArrayList<>(); + final int windowingMode = getWindowingMode(); + final boolean isAssistantType = isActivityTypeAssistant(); + for (int i = parent.getChildCount() - 1; i >= 0; --i) { + final WindowContainer other = parent.getChildAt(i); + if (other == null) continue; + + final boolean hasRunningActivities = hasRunningActivity(other); + if (other == this) { + // Should be visible if there is no other fragment occluding it, unless it doesn't + // have any running activities, not starting one and not home stack. + shouldBeVisible = hasRunningActivities + || (starting != null && starting.isDescendantOf(this)) + || isActivityTypeHome(); + break; + } + + if (!hasRunningActivities) { + continue; + } + + final int otherWindowingMode = other.getWindowingMode(); + if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) { + if (isTranslucent(other, starting)) { + // Can be visible behind a translucent fullscreen TaskFragment. + gotTranslucentFullscreen = true; + continue; + } + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW + && other.matchParentBounds()) { + if (isTranslucent(other, starting)) { + // Can be visible behind a translucent TaskFragment. + gotTranslucentFullscreen = true; + continue; + } + // Multi-window TaskFragment that matches parent bounds would occlude other children + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + && !gotOpaqueSplitScreenPrimary) { + gotRootSplitScreenFragment = true; + gotTranslucentSplitScreenPrimary = isTranslucent(other, starting); + gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary; + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + && gotOpaqueSplitScreenPrimary) { + // Can't be visible behind another opaque TaskFragment in split-screen-primary. + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + && !gotOpaqueSplitScreenSecondary) { + gotRootSplitScreenFragment = true; + gotTranslucentSplitScreenSecondary = isTranslucent(other, starting); + gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary; + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + && gotOpaqueSplitScreenSecondary) { + // Can't be visible behind another opaque TaskFragment in split-screen-secondary + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + } + if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) { + // Can not be visible if we are in split-screen windowing mode and both halves of + // the screen are opaque. + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + if (isAssistantType && gotRootSplitScreenFragment) { + // Assistant TaskFragment can't be visible behind split-screen. In addition to + // this not making sense, it also works around an issue here we boost the z-order + // of the assistant window surfaces in window manager whenever it is visible. + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + + final TaskFragment otherTaskFrag = other.asTaskFragment(); + if (otherTaskFrag != null && otherTaskFrag.mAdjacentTaskFragment != null) { + if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) { + if (otherTaskFrag.isTranslucent(starting) + || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) { + // Can be visible behind a translucent adjacent TaskFragments. + gotTranslucentFullscreen = true; + continue; + } + // Can not be visible behind adjacent TaskFragments. + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } else { + adjacentTaskFragments.add(otherTaskFrag); + } + } + + } + + if (!shouldBeVisible) { + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + + // Handle cases when there can be a translucent split-screen TaskFragment on top. + switch (windowingMode) { + case WINDOWING_MODE_FULLSCREEN: + if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) { + // At least one of the split-screen TaskFragment that covers this one is + // translucent. + // When in split mode, home will be reparented to the secondary split while + // leaving TaskFragments not supporting split below. Due to + // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to + // the bottom, this makes sure TaskFragments not in split roots won't occlude + // home task unexpectedly. + return TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + break; + case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: + if (gotTranslucentSplitScreenPrimary) { + // Covered by translucent primary split-screen on top. + return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; + } + break; + case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: + if (gotTranslucentSplitScreenSecondary) { + // Covered by translucent secondary split-screen on top. + return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; + } + break; + } + + // Lastly - check if there is a translucent fullscreen TaskFragment on top. + return gotTranslucentFullscreen + ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT + : TASK_FRAGMENT_VISIBILITY_VISIBLE; + } + + private static boolean hasRunningActivity(WindowContainer wc) { + if (wc.asTaskFragment() != null) { + return wc.asTaskFragment().topRunningActivity() != null; + } + return wc.asActivityRecord() != null && !wc.asActivityRecord().finishing; + } + + private static boolean isTranslucent(WindowContainer wc, ActivityRecord starting) { + if (wc.asTaskFragment() != null) { + return wc.asTaskFragment().isTranslucent(starting); + } else if (wc.asActivityRecord() != null) { + return !wc.asActivityRecord().occludesParent(); + } + return false; + } + + + private boolean isTopActivityLaunchedBehind() { + final ActivityRecord top = topRunningActivity(); + return top != null && top.mLaunchTaskBehind; + } + + final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges, + boolean preserveWindows, boolean notifyClients) { + mTaskSupervisor.beginActivityVisibilityUpdate(); + try { + mEnsureActivitiesVisibleHelper.process( + starting, configChanges, preserveWindows, notifyClients); + } finally { + mTaskSupervisor.endActivityVisibilityUpdate(); + } + } + + final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, + boolean deferPause) { + ActivityRecord next = topRunningActivity(true /* focusableOnly */); + if (next == null || !next.canResumeByCompat()) { + return false; + } + + next.delayedResume = false; + final TaskDisplayArea taskDisplayArea = getDisplayArea(); + + // If the top activity is the resumed one, nothing to do. + if (mResumedActivity == next && next.isState(RESUMED) + && taskDisplayArea.allResumedActivitiesComplete()) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible + // we still want to check if the visibility of other windows have changed (e.g. bringing + // a fullscreen window forward to cover another freeform activity.) + if (taskDisplayArea.inMultiWindowMode()) { + taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */, true /* notifyClients */); + } + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity " + + "resumed %s", next); + return false; + } + + // If we are currently pausing an activity, then don't do anything until that is done. + final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete(); + if (!allPausedComplete) { + ProtoLog.v(WM_DEBUG_STATES, + "resumeTopActivity: Skip resume: some activity pausing."); + return false; + } + + // If we are sleeping, and there is no resumed activity, and the top activity is paused, + // well that is the state we want. + if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) { + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and" + + " all paused"); + return false; + } + + // Make sure that the user who owns this activity is started. If not, + // we will just leave it as is because someone should be bringing + // another user's activities to the top of the stack. + if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) { + Slog.w(TAG, "Skipping resume of top activity " + next + + ": user " + next.mUserId + " is stopped"); + return false; + } + + // The activity may be waiting for stop, but that is no longer + // appropriate for it. + mTaskSupervisor.mStoppingActivities.remove(next); + + if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); + + mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); + + ActivityRecord lastResumed = null; + final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask(); + if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) { + // So, why aren't we using prev here??? See the param comment on the method. prev + // doesn't represent the last resumed activity. However, the last focus stack does if + // it isn't null. + lastResumed = lastFocusedRootTask.getTopResumedActivity(); + } + + boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next); + if (mResumedActivity != null) { + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity); + pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */, + next, "resumeTopActivity"); + } + if (pausing) { + ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to" + + " start pausing"); + // At this point we want to put the upcoming activity's process + // at the top of the LRU list, since we know we will be needing it + // very soon and it would be a waste to let it get killed if it + // happens to be sitting towards the end. + if (next.attachedToProcess()) { + next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, + true /* activityChange */, false /* updateOomAdj */, + false /* addPendingTopUid */); + } else if (!next.isProcessRunning()) { + // Since the start-process is asynchronous, if we already know the process of next + // activity isn't running, we can start the process earlier to save the time to wait + // for the current activity to be paused. + final boolean isTop = this == taskDisplayArea.getFocusedRootTask(); + mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop, + isTop ? "pre-top-activity" : "pre-activity"); + } + if (lastResumed != null) { + lastResumed.setWillCloseOrEnterPip(true); + } + return true; + } else if (mResumedActivity == next && next.isState(RESUMED) + && taskDisplayArea.allResumedActivitiesComplete()) { + // It is possible for the activity to be resumed when we paused back stacks above if the + // next activity doesn't have to wait for pause to complete. + // So, nothing else to-do except: + // Make sure we have executed any pending transitions, since there + // should be nothing left to do at this point. + executeAppTransition(options); + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed " + + "(dontWaitForPause) %s", next); + return true; + } + + // If the most recent activity was noHistory but was only stopped rather + // than stopped+finished because the device went to sleep, we need to make + // sure to finish it as we're making a new activity topmost. + if (shouldSleepActivities()) { + mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next); + } + + if (prev != null && prev != next && next.nowVisible) { + // The next activity is already visible, so hide the previous + // activity's windows right now so we can show the new one ASAP. + // We only do this if the previous is finishing, which should mean + // it is on top of the one being resumed so hiding it quickly + // is good. Otherwise, we want to do the normal route of allowing + // the resumed activity to be shown so we can decide if the + // previous should actually be hidden depending on whether the + // new one is found to be full-screen or not. + if (prev.finishing) { + prev.setVisibility(false); + if (DEBUG_SWITCH) { + Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev + + ", nowVisible=" + next.nowVisible); + } + } else { + if (DEBUG_SWITCH) { + Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev + + ", nowVisible=" + next.nowVisible); + } + } + } + + // Launching this app's activity, make sure the app is no longer + // considered stopped. + try { + mTaskSupervisor.getActivityMetricsLogger() + .notifyBeforePackageUnstopped(next.packageName); + mAtmService.getPackageManager().setPackageStoppedState( + next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */ + } catch (RemoteException e1) { + } catch (IllegalArgumentException e) { + Slog.w(TAG, "Failed trying to unstop package " + + next.packageName + ": " + e); + } + + // We are starting up the next activity, so tell the window manager + // that the previous one will be hidden soon. This way it can know + // to ignore it when computing the desired screen orientation. + boolean anim = true; + final DisplayContent dc = taskDisplayArea.mDisplayContent; + if (prev != null) { + if (prev.finishing) { + if (DEBUG_TRANSITION) { + Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev); + } + if (mTaskSupervisor.mNoAnimActivities.contains(prev)) { + anim = false; + dc.prepareAppTransition(TRANSIT_NONE); + } else { + dc.prepareAppTransition(TRANSIT_CLOSE); + } + prev.setVisibility(false); + } else { + if (DEBUG_TRANSITION) { + Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev); + } + if (mTaskSupervisor.mNoAnimActivities.contains(next)) { + anim = false; + dc.prepareAppTransition(TRANSIT_NONE); + } else { + dc.prepareAppTransition(TRANSIT_OPEN, + next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); + } + } + } else { + if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous"); + if (mTaskSupervisor.mNoAnimActivities.contains(next)) { + anim = false; + dc.prepareAppTransition(TRANSIT_NONE); + } else { + dc.prepareAppTransition(TRANSIT_OPEN); + } + } + + if (anim) { + next.applyOptionsAnimation(); + } else { + next.abortAndClearOptionsAnimation(); + } + + mTaskSupervisor.mNoAnimActivities.clear(); + + if (next.attachedToProcess()) { + if (DEBUG_SWITCH) { + Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped + + " visibleRequested=" + next.mVisibleRequested); + } + + // If the previous activity is translucent, force a visibility update of + // the next activity, so that it's added to WM's opening app list, and + // transition animation can be set up properly. + // For example, pressing Home button with a translucent activity in focus. + // Launcher is already visible in this case. If we don't add it to opening + // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a + // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation. + final boolean lastActivityTranslucent = inMultiWindowMode() + || mLastPausedActivity != null && !mLastPausedActivity.occludesParent(); + + // This activity is now becoming visible. + if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) { + next.setVisibility(true); + } + + // schedule launch ticks to collect information about slow apps. + next.startLaunchTickingLocked(); + + ActivityRecord lastResumedActivity = + lastFocusedRootTask == null ? null + : lastFocusedRootTask.getTopResumedActivity(); + final ActivityRecord.State lastState = next.getState(); + + mAtmService.updateCpuStats(); + + ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next); + + next.setState(RESUMED, "resumeTopActivity"); + + // Have the window manager re-evaluate the orientation of + // the screen based on the new activity order. + boolean notUpdated = true; + + // Activity should also be visible if set mLaunchTaskBehind to true (see + // ActivityRecord#shouldBeVisibleIgnoringKeyguard()). + if (shouldBeVisible(next)) { + // We have special rotation behavior when here is some active activity that + // requests specific orientation or Keyguard is locked. Make sure all activity + // visibilities are set correctly as well as the transition is updated if needed + // to get the correct rotation behavior. Otherwise the following call to update + // the orientation may cause incorrect configurations delivered to client as a + // result of invisible window resize. + // TODO: Remove this once visibilities are set correctly immediately when + // starting an activity. + notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(), + true /* markFrozenIfConfigChanged */, false /* deferResume */); + } + + if (notUpdated) { + // The configuration update wasn't able to keep the existing + // instance of the activity, and instead started a new one. + // We should be all done, but let's just make sure our activity + // is still at the top and schedule another run if something + // weird happened. + ActivityRecord nextNext = topRunningActivity(); + ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: " + + "%s, new next: %s", next, nextNext); + if (nextNext != next) { + // Do over! + mTaskSupervisor.scheduleResumeTopActivities(); + } + if (!next.mVisibleRequested || next.stopped) { + next.setVisibility(true); + } + next.completeResumeLocked(); + return true; + } + + try { + final ClientTransaction transaction = + ClientTransaction.obtain(next.app.getThread(), next.appToken); + // Deliver all pending results. + ArrayList<ResultInfo> a = next.results; + if (a != null) { + final int size = a.size(); + if (!next.finishing && size > 0) { + if (DEBUG_RESULTS) { + Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a); + } + transaction.addCallback(ActivityResultItem.obtain(a)); + } + } + + if (next.newIntents != null) { + transaction.addCallback( + NewIntentItem.obtain(next.newIntents, true /* resume */)); + } + + // Well the app will no longer be stopped. + // Clear app token stopped state in window manager if needed. + next.notifyAppResumed(next.stopped); + + EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next), + next.getTask().mTaskId, next.shortComponentName); + + mAtmService.getAppWarningsLocked().onResumeActivity(next); + next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState); + next.abortAndClearOptionsAnimation(); + transaction.setLifecycleStateRequest( + ResumeActivityItem.obtain(next.app.getReportedProcState(), + dc.isNextTransitionForward())); + mAtmService.getLifecycleManager().scheduleTransaction(transaction); + + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next); + } catch (Exception e) { + // Whoops, need to restart this activity! + ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: " + + "%s", lastState, next); + next.setState(lastState, "resumeTopActivityInnerLocked"); + + // lastResumedActivity being non-null implies there is a lastStack present. + if (lastResumedActivity != null) { + lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked"); + } + + Slog.i(TAG, "Restarting because process died: " + next); + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null + && lastFocusedRootTask.isTopRootTaskInDisplayArea()) { + next.showStartingWindow(false /* taskSwitch */); + } + mTaskSupervisor.startSpecificActivity(next, true, false); + return true; + } + + // From this point on, if something goes wrong there is no way + // to recover the activity. + try { + next.completeResumeLocked(); + } catch (Exception e) { + // If any exception gets thrown, toss away this + // activity and try the next one. + Slog.w(TAG, "Exception thrown during resume of " + next, e); + next.finishIfPossible("resume-exception", true /* oomAdj */); + return true; + } + } else { + // Whoops, need to restart this activity! + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else { + if (SHOW_APP_STARTING_PREVIEW) { + next.showStartingWindow(false /* taskSwich */); + } + if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next); + } + ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next); + mTaskSupervisor.startSpecificActivity(next, true, true); + } + + return true; + } + + boolean shouldSleepOrShutDownActivities() { + return shouldSleepActivities() || mAtmService.mShuttingDown; + } + + /** + * Returns true if the TaskFragment should be visible. + * + * @param starting The currently starting activity or null if there is none. + */ + boolean shouldBeVisible(ActivityRecord starting) { + return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE; + } + + final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) { + return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason); + } + + /** + * Start pausing the currently resumed activity. It is an error to call this if there + * is already an activity being paused or there is no resumed activity. + * + * @param userLeaving True if this should result in an onUserLeaving to the current activity. + * @param uiSleeping True if this is happening with the user interface going to sleep (the + * screen turning off). + * @param resuming The activity we are currently trying to resume or null if this is not being + * called as part of resuming the top activity, so we shouldn't try to instigate + * a resume here if not null. + * @param reason The reason of pausing the activity. + * @return Returns true if an activity now is in the PAUSING state, and we are waiting for + * it to tell us when it is done. + */ + boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming, + String reason) { + if (!hasDirectChildActivities()) { + return false; + } + + ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this, + mResumedActivity); + + if (mPausingActivity != null) { + Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity + + " state=" + mPausingActivity.getState()); + if (!shouldSleepActivities()) { + // Avoid recursion among check for sleep and complete pause during sleeping. + // Because activity will be paused immediately after resume, just let pause + // be completed by the order of activity paused from clients. + completePause(false, resuming); + } + } + ActivityRecord prev = mResumedActivity; + + if (prev == null) { + if (resuming == null) { + Slog.wtf(TAG, "Trying to pause when nothing is resumed"); + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + return false; + } + + if (prev == resuming) { + Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed"); + return false; + } + + ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); + mPausingActivity = prev; + mLastPausedActivity = prev; + if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { + mTaskSupervisor.mNoHistoryActivities.add(prev); + } + prev.setState(PAUSING, "startPausingLocked"); + prev.getTask().touchActiveTime(); + + mAtmService.updateCpuStats(); + + boolean pauseImmediately = false; + boolean shouldAutoPip = false; + if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) { + // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous + // activity to be paused, while at the same time resuming the new resume activity + // only if the previous activity can't go into Pip since we want to give Pip + // activities a chance to enter Pip before resuming the next activity. + final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState( + "shouldResumeWhilePausing", userLeaving); + if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) { + shouldAutoPip = true; + } else if (!lastResumedCanPip) { + pauseImmediately = true; + } else { + // The previous activity may still enter PIP even though it did not allow auto-PIP. + } + } + + boolean didAutoPip = false; + if (prev.attachedToProcess()) { + if (shouldAutoPip) { + ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " + + "directly: %s", prev); + + didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs); + mPausingActivity = null; + } else { + ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev); + try { + EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), + prev.shortComponentName, "userLeaving=" + userLeaving, reason); + + mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), + prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, + prev.configChangeFlags, pauseImmediately)); + } catch (Exception e) { + // Ignore exception, if process died other code will cleanup. + Slog.w(TAG, "Exception thrown during pause", e); + mPausingActivity = null; + mLastPausedActivity = null; + mTaskSupervisor.mNoHistoryActivities.remove(prev); + } + } + } else { + mPausingActivity = null; + mLastPausedActivity = null; + mTaskSupervisor.mNoHistoryActivities.remove(prev); + } + + // If we are not going to sleep, we want to ensure the device is + // awake until the next activity is started. + if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) { + mTaskSupervisor.acquireLaunchWakelock(); + } + + // If already entered PIP mode, no need to keep pausing. + if (mPausingActivity != null && !didAutoPip) { + // Have the window manager pause its key dispatching until the new + // activity has started. If we're pausing the activity just because + // the screen is being turned off and the UI is sleeping, don't interrupt + // key dispatch; the same activity will pick it up again on wakeup. + if (!uiSleeping) { + prev.pauseKeyDispatchingLocked(); + } else { + ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off"); + } + + if (pauseImmediately) { + // If the caller said they don't want to wait for the pause, then complete + // the pause now. + completePause(false, resuming); + return false; + + } else { + prev.schedulePauseTimeout(); + return true; + } + + } else { + // This activity either failed to schedule the pause or it entered PIP mode, + // so just treat it as being paused now. + ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next."); + if (resuming == null) { + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + return false; + } + } + + @VisibleForTesting + void completePause(boolean resumeNext, ActivityRecord resuming) { + // Complete the pausing process of a pausing activity, so it doesn't make sense to + // operate on non-leaf tasks. + // warnForNonLeafTask("completePauseLocked"); + + ActivityRecord prev = mPausingActivity; + ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev); + + if (prev != null) { + prev.setWillCloseOrEnterPip(false); + final boolean wasStopping = prev.isState(STOPPING); + prev.setState(PAUSED, "completePausedLocked"); + if (prev.finishing) { + // We will update the activity visibility later, no need to do in + // completeFinishing(). Updating visibility here might also making the next + // activities to be resumed, and could result in wrong app transition due to + // lack of previous activity information. + ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev); + prev = prev.completeFinishing(false /* updateVisibility */, + "completePausedLocked"); + } else if (prev.hasProcess()) { + ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s " + + "wasStopping=%b visibleRequested=%b", prev, wasStopping, + prev.mVisibleRequested); + if (prev.deferRelaunchUntilPaused) { + // Complete the deferred relaunch that was waiting for pause to complete. + ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev); + prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch); + } else if (wasStopping) { + // We are also stopping, the stop request must have gone soon after the pause. + // We can't clobber it, because the stop confirmation will not be handled. + // We don't need to schedule another stop, we only need to let it happen. + prev.setState(STOPPING, "completePausedLocked"); + } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) { + // Clear out any deferred client hide we might currently have. + prev.setDeferHidingClient(false); + // If we were visible then resumeTopActivities will release resources before + // stopping. + prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */, + "completePauseLocked"); + } + } else { + ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev); + prev = null; + } + // It is possible the activity was freezing the screen before it was paused. + // In that case go ahead and remove the freeze this activity has on the screen + // since it is no longer visible. + if (prev != null) { + prev.stopFreezingScreenLocked(true /*force*/); + } + mPausingActivity = null; + } + + if (resumeNext) { + final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); + if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) { + mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, + null /* targetOptions */); + } else { + // checkReadyForSleep(); + final ActivityRecord top = + topRootTask != null ? topRootTask.topRunningActivity() : null; + if (top == null || (prev != null && top != prev)) { + // If there are no more activities available to run, do resume anyway to start + // something. Also if the top activity on the root task is not the just paused + // activity, we need to go ahead and resume it to ensure we complete an + // in-flight app switch. + mRootWindowContainer.resumeFocusedTasksTopActivities(); + } + } + } + + if (prev != null) { + prev.resumeKeyDispatchingLocked(); + } + + mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); + + // Notify when the task stack has changed, but only if visibilities changed (not just + // focus). Also if there is an active root pinned task - we always want to notify it about + // task stack changes, because its positioning may depend on it. + if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause + || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) { + mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged(); + mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false; + } + } + + @Override + void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) { + super.forAllTaskFragments(callback, traverseTopToBottom); + callback.accept(this); + } + + @Override + void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) { + final int count = mChildren.size(); + boolean isLeafTaskFrag = true; + if (traverseTopToBottom) { + for (int i = count - 1; i >= 0; --i) { + final TaskFragment child = mChildren.get(i).asTaskFragment(); + if (child != null) { + isLeafTaskFrag = false; + child.forAllLeafTaskFragments(callback, traverseTopToBottom); + } + } + } else { + for (int i = 0; i < count; i++) { + final TaskFragment child = mChildren.get(i).asTaskFragment(); + if (child != null) { + isLeafTaskFrag = false; + child.forAllLeafTaskFragments(callback, traverseTopToBottom); + } + } + } + if (isLeafTaskFrag) callback.accept(this); + } + + @Override + boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) { + boolean isLeafTaskFrag = true; + for (int i = mChildren.size() - 1; i >= 0; --i) { + final TaskFragment child = mChildren.get(i).asTaskFragment(); + if (child != null) { + isLeafTaskFrag = false; + if (child.forAllLeafTaskFragments(callback)) { + return true; + } + } + } + if (isLeafTaskFrag) { + return callback.apply(this); + } + return false; + } + + void addChild(ActivityRecord r) { + addChild(r, POSITION_TOP); + } + + @Override + void addChild(WindowContainer child, int index) { + boolean isAddingActivity = child.asActivityRecord() != null; + final Task task = isAddingActivity ? getTask() : null; + + // If this task had any child before we added this one. + boolean taskHadChild = task != null && task.hasChild(); + // getActivityType() looks at the top child, so we need to read the type before adding + // a new child in case the new child is on top and UNDEFINED. + final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED; + + super.addChild(child, index); + + if (isAddingActivity && task != null) { + child.asActivityRecord().inHistory = true; + task.onDescendantActivityAdded(taskHadChild, activityType, child.asActivityRecord()); + } + } + + void executeAppTransition(ActivityOptions options) { + // No app transition applied to the task fragment. + } + + boolean shouldSleepActivities() { + return false; + } + + @Override + void resolveOverrideConfiguration(Configuration newParentConfig) { + mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds()); + super.resolveOverrideConfiguration(newParentConfig); + + int windowingMode = + getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); + final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode(); + + // Resolve override windowing mode to fullscreen for home task (even on freeform + // display), or split-screen if in split-screen mode. + if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) { + windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode) + ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN; + getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode); + } + + // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in + // pinned windowing mode. + if (!supportsMultiWindow()) { + final int candidateWindowingMode = + windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode; + if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode) + && candidateWindowingMode != WINDOWING_MODE_PINNED) { + getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode( + WINDOWING_MODE_FULLSCREEN); + } + } + + if (isLeafTaskFragment()) { + resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */); + } + computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); + } + + boolean supportsMultiWindow() { + return supportsMultiWindowInDisplayArea(getDisplayArea()); + } + + /** + * @return whether this task supports multi-window if it is in the given + * {@link TaskDisplayArea}. + */ + boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) { + if (!mAtmService.mSupportsMultiWindow) { + return false; + } + final Task task = getTask(); + if (task == null) { + return false; + } + if (tda == null) { + Slog.w(TAG, "Can't find TaskDisplayArea to determine support for multi" + + " window. Task id=" + getTaskId() + " attached=" + isAttached()); + return false; + } + if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) { + // Not support non-resizable in multi window. + return false; + } + + return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight); + } + + private int getTaskId() { + return getTask() != null ? getTask().mTaskId : INVALID_TASK_ID; + } + + private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig, + Rect previousBounds) { + + int windowingMode = + getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + windowingMode = newParentConfig.windowConfiguration.getWindowingMode(); + } + // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old + // mode that may cause the bounds to be miscalculated, e.g. letterboxed. + getConfiguration().windowConfiguration.setWindowingMode(windowingMode); + Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds(); + + if (windowingMode == WINDOWING_MODE_FULLSCREEN) { + // Use empty bounds to indicate "fill parent". + outOverrideBounds.setEmpty(); + // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if + // the parent or display is smaller than the size, the content may be cropped. + return; + } + + adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig); + if (windowingMode == WINDOWING_MODE_FREEFORM) { + computeFreeformBounds(outOverrideBounds, newParentConfig); + return; + } + } + + /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */ + private void computeFreeformBounds(@NonNull Rect outBounds, + @NonNull Configuration newParentConfig) { + // by policy, make sure the window remains within parent somewhere + final float density = + ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT; + final Rect parentBounds = + new Rect(newParentConfig.windowConfiguration.getBounds()); + final DisplayContent display = getDisplayContent(); + if (display != null) { + // If a freeform window moves below system bar, there is no way to move it again + // by touch. Because its caption is covered by system bar. So we exclude them + // from root task bounds. and then caption will be shown inside stable area. + final Rect stableBounds = new Rect(); + display.getStableRect(stableBounds); + parentBounds.intersect(stableBounds); + } + + fitWithinBounds(outBounds, parentBounds, + (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP), + (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP)); + + // Prevent to overlap caption with stable insets. + final int offsetTop = parentBounds.top - outBounds.top; + if (offsetTop > 0) { + outBounds.offset(0, offsetTop); + } + } + + /** + * Adjusts bounds to stay within root task bounds. + * + * Since bounds might be outside of root task bounds, this method tries to move the bounds in + * a way that keep them unchanged, but be contained within the root task bounds. + * + * @param bounds Bounds to be adjusted. + * @param rootTaskBounds Bounds within which the other bounds should remain. + * @param overlapPxX The amount of px required to be visible in the X dimension. + * @param overlapPxY The amount of px required to be visible in the Y dimension. + */ + private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX, + int overlapPxY) { + if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) { + return; + } + + // For each side of the parent (eg. left), check if the opposing side of the window (eg. + // right) is at least overlap pixels away. If less, offset the window by that difference. + int horizontalDiff = 0; + // If window is smaller than overlap, use it's smallest dimension instead + int overlapLR = Math.min(overlapPxX, bounds.width()); + if (bounds.right < (rootTaskBounds.left + overlapLR)) { + horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left); + } else if (bounds.left > (rootTaskBounds.right - overlapLR)) { + horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left)); + } + int verticalDiff = 0; + int overlapTB = Math.min(overlapPxY, bounds.width()); + if (bounds.bottom < (rootTaskBounds.top + overlapTB)) { + verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top); + } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) { + verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top)); + } + bounds.offset(horizontalDiff, verticalDiff); + } + + /** + * Ensures all visible activities at or below the input activity have the right configuration. + */ + void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) { + mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow); + } + + void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds, + @NonNull Configuration parentConfig) { + int minWidth = mMinWidth; + int minHeight = mMinHeight; + // If the task has no requested minimal size, we'd like to enforce a minimal size + // so that the user can not render the task fragment too small to manipulate. We don't need + // to do this for the root pinned task as the bounds are controlled by the system. + if (!inPinnedWindowingMode()) { + final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp; + final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT; + final int defaultMinSize = (int) (defaultMinSizeDp * density); + + if (minWidth == INVALID_MIN_SIZE) { + minWidth = defaultMinSize; + } + if (minHeight == INVALID_MIN_SIZE) { + minHeight = defaultMinSize; + } + } + if (bounds.isEmpty()) { + // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they + // do, we can just skip. + final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); + if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) { + return; + } + bounds.set(parentBounds); + } + final boolean adjustWidth = minWidth > bounds.width(); + final boolean adjustHeight = minHeight > bounds.height(); + if (!(adjustWidth || adjustHeight)) { + return; + } + + if (adjustWidth) { + if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) { + bounds.left = bounds.right - minWidth; + } else { + // Either left bounds match, or neither match, or the previous bounds were + // fullscreen and we default to keeping left. + bounds.right = bounds.left + minWidth; + } + } + if (adjustHeight) { + if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) { + bounds.top = bounds.bottom - minHeight; + } else { + // Either top bounds match, or neither match, or the previous bounds were + // fullscreen and we default to keeping top. + bounds.bottom = bounds.top + minHeight; + } + } + } + + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig) { + computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, + null /* compatInsets */); + } + + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) { + if (overrideDisplayInfo != null) { + // Make sure the screen related configs can be computed by the provided display info. + inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; + invalidateAppBoundsConfig(inOutConfig); + } + computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo, + null /* compatInsets */); + } + + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig, + @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + if (compatInsets != null) { + // Make sure the app bounds can be computed by the compat insets. + invalidateAppBoundsConfig(inOutConfig); + } + computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, + compatInsets); + } + + /** + * Forces the app bounds related configuration can be computed by + * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo, + * ActivityRecord.CompatDisplayInsets)}. + */ + private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) { + final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds(); + if (appBounds != null) { + appBounds.setEmpty(); + } + inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED; + inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED; + } + + /** + * Calculates configuration values used by the client to get resources. This should be run + * using app-facing bounds (bounds unmodified by animations or transient interactions). + * + * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely + * configuring an "inherit-bounds" window which means that all configuration settings would + * just be inherited from the parent configuration. + **/ + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo, + @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + windowingMode = parentConfig.windowConfiguration.getWindowingMode(); + } + + float density = inOutConfig.densityDpi; + if (density == Configuration.DENSITY_DPI_UNDEFINED) { + density = parentConfig.densityDpi; + } + density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; + + // The bounds may have been overridden at this level. If the parent cannot cover these + // bounds, the configuration is still computed according to the override bounds. + final boolean insideParentBounds; + + final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); + final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds(); + if (resolvedBounds == null || resolvedBounds.isEmpty()) { + mTmpFullBounds.set(parentBounds); + insideParentBounds = true; + } else { + mTmpFullBounds.set(resolvedBounds); + insideParentBounds = parentBounds.contains(resolvedBounds); + } + + // Non-null compatibility insets means the activity prefers to keep its original size, so + // out bounds doesn't need to be restricted by the parent or current display + final boolean customContainerPolicy = compatInsets != null; + + Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); + if (outAppBounds == null || outAppBounds.isEmpty()) { + // App-bounds hasn't been overridden, so calculate a value for it. + inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds); + outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); + + if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { + final Rect containingAppBounds; + if (insideParentBounds) { + containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); + } else { + // Restrict appBounds to display non-decor rather than parent because the + // override bounds are beyond the parent. Otherwise, it won't match the + // overridden bounds. + final TaskDisplayArea displayArea = getDisplayArea(); + containingAppBounds = displayArea != null + ? displayArea.getWindowConfiguration().getAppBounds() : null; + } + if (containingAppBounds != null && !containingAppBounds.isEmpty()) { + outAppBounds.intersect(containingAppBounds); + } + } + } + + if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED + || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { + if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) { + mTmpNonDecorBounds.set(mTmpFullBounds); + mTmpStableBounds.set(mTmpFullBounds); + } else if (!customContainerPolicy + && (overrideDisplayInfo != null || getDisplayContent() != null)) { + final DisplayInfo di = overrideDisplayInfo != null + ? overrideDisplayInfo + : getDisplayContent().getDisplayInfo(); + + // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen + // area, i.e. the screen area without the system bars. + // The non decor inset are areas that could never be removed in Honeycomb. See + // {@link WindowManagerPolicy#getNonDecorInsetsLw}. + calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di); + } else { + // Apply the given non-decor and stable insets to calculate the corresponding bounds + // for screen size of configuration. + int rotation = inOutConfig.windowConfiguration.getRotation(); + if (rotation == ROTATION_UNDEFINED) { + rotation = parentConfig.windowConfiguration.getRotation(); + } + if (rotation != ROTATION_UNDEFINED && customContainerPolicy) { + mTmpNonDecorBounds.set(mTmpFullBounds); + mTmpStableBounds.set(mTmpFullBounds); + compatInsets.getBoundsByRotation(mTmpBounds, rotation); + intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds, + compatInsets.mNonDecorInsets[rotation]); + intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds, + compatInsets.mStableInsets[rotation]); + outAppBounds.set(mTmpNonDecorBounds); + } else { + // Set to app bounds because it excludes decor insets. + mTmpNonDecorBounds.set(outAppBounds); + mTmpStableBounds.set(outAppBounds); + } + } + + if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { + final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density); + inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy) + ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp) + : overrideScreenWidthDp; + } + if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { + final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); + inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy) + ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp) + : overrideScreenHeightDp; + } + + if (inOutConfig.smallestScreenWidthDp + == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { + if (WindowConfiguration.isFloating(windowingMode)) { + // For floating tasks, calculate the smallest width from the bounds of the task + inOutConfig.smallestScreenWidthDp = (int) ( + Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density); + } + // otherwise, it will just inherit + } + } + + if (inOutConfig.orientation == ORIENTATION_UNDEFINED) { + inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp) + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + } + if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) { + // For calculating screen layout, we need to use the non-decor inset screen area for the + // calculation for compatibility reasons, i.e. screen area without system bars that + // could never go away in Honeycomb. + int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density); + int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density); + // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is + // undefined so it can't be used. + if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { + compatScreenWidthDp = inOutConfig.screenWidthDp; + } + if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { + compatScreenHeightDp = inOutConfig.screenHeightDp; + } + // Reducing the screen layout starting from its parent config. + inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout, + compatScreenWidthDp, compatScreenHeightDp); + } + } + + /** + * Gets bounds with non-decor and stable insets applied respectively. + * + * If bounds overhangs the display, those edges will not get insets. See + * {@link #intersectWithInsetsIfFits} + * + * @param outNonDecorBounds where to place bounds with non-decor insets applied. + * @param outStableBounds where to place bounds with stable insets applied. + * @param bounds the bounds to inset. + */ + void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds, + DisplayInfo displayInfo) { + outNonDecorBounds.set(bounds); + outStableBounds.set(bounds); + final Task rootTask = getRootTaskFragment().asTask(); + if (rootTask == null || rootTask.mDisplayContent == null) { + return; + } + mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + + final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy(); + policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, + displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets); + intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets); + + policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation); + intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets); + } + + /** + * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than + * intersectBounds on a side, then the respective side will not be intersected. + * + * The assumption is that if inOutBounds is initially larger than intersectBounds, then the + * inset on that side is no-longer applicable. This scenario happens when a task's minimal + * bounds are larger than the provided parent/display bounds. + * + * @param inOutBounds the bounds to intersect. + * @param intersectBounds the bounds to intersect with. + * @param intersectInsets insets to apply to intersectBounds before intersecting. + */ + static void intersectWithInsetsIfFits( + Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) { + if (inOutBounds.right <= intersectBounds.right) { + inOutBounds.right = + Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right); + } + if (inOutBounds.bottom <= intersectBounds.bottom) { + inOutBounds.bottom = + Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom); + } + if (inOutBounds.left >= intersectBounds.left) { + inOutBounds.left = + Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left); + } + if (inOutBounds.top >= intersectBounds.top) { + inOutBounds.top = + Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top); + } + } + + /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */ + static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp, + int screenHeightDp) { + sourceScreenLayout = sourceScreenLayout + & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK); + final int longSize = Math.max(screenWidthDp, screenHeightDp); + final int shortSize = Math.min(screenWidthDp, screenHeightDp); + return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize); + } + + @Override + public int getActivityType() { + final int applicationType = super.getActivityType(); + if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) { + return applicationType; + } + return getTopChild().getActivityType(); + } + + @Override + public void onConfigurationChanged(Configuration newParentConfig) { + super.onConfigurationChanged(newParentConfig); + + if (mTaskFragmentOrganizer != null) { + // Parent config may have changed. The controller will check if there is any important + // config change for the organizer. + mTaskFragmentOrganizerController + .onTaskFragmentParentInfoChanged(mTaskFragmentOrganizer, this); + mTaskFragmentOrganizerController + .onTaskFragmentInfoChanged(mTaskFragmentOrganizer, this); + } + } + + // TODO(b/190433129) call when TaskFragment is created from WCT#createTaskFragment + private void sendTaskFragmentAppeared() { + if (mTaskFragmentOrganizer != null) { + mTaskFragmentOrganizerController.onTaskFragmentAppeared(mTaskFragmentOrganizer, this); + } + } + + // TODO(b/190433129) call when TaskFragment is removed from WCT#deleteTaskFragment + private void sendTaskFragmentVanished() { + if (mTaskFragmentOrganizer != null) { + mTaskFragmentOrganizerController.onTaskFragmentVanished(mTaskFragmentOrganizer, this); + } + } + + /** + * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be + * called from {@link Task}. + */ + TaskFragmentInfo getTaskFragmentInfo() { + return new TaskFragmentInfo( + mFragmentToken, + mInitialComponentName, + mRemoteToken.toWindowContainerToken(), + getConfiguration(), + getChildCount() == 0, + isVisible()); + } + + @Nullable + IBinder getFragmentToken() { + return mFragmentToken; + } + + /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */ + void clearLastPausedActivity() { + forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null); + } + + /** + * Sets {@link #mMinWidth} and {@link #mMinWidth} to this TaskFragment. + * It is usually set from the parent {@link Task} when adding the TaskFragment to the window + * hierarchy. + */ + void setMinDimensions(int minWidth, int minHeight) { + if (asTask() != null) { + throw new UnsupportedOperationException("This method must not be used to Task. The " + + " minimum dimension of Task should be passed from Task constructor."); + } + mMinWidth = minWidth; + mMinHeight = minHeight; + } + + boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll, + boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) { + boolean printed = false; + Runnable headerPrinter = () -> { + if (needSep) { + pw.println(); + } + if (header != null) { + header.run(); + } + + dumpInner(prefix, pw, dumpAll, dumpPackage); + }; + + if (dumpPackage == null) { + // If we are not filtering by package, we want to print absolutely everything, + // so always print the header even if there are no tasks/activities inside. + headerPrinter.run(); + headerPrinter = null; + printed = true; + } + + for (int i = mChildren.size() - 1; i >= 0; --i) { + WindowContainer child = mChildren.get(i); + if (child.asTaskFragment() != null) { + printed |= child.asTaskFragment().dump(prefix + " ", fd, pw, dumpAll, + dumpClient, dumpPackage, needSep, headerPrinter); + } else if (child.asActivityRecord() != null) { + ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + " ", + "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter, + getTask()); + } + } + + return printed; + } + + void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) { + pw.print(prefix); pw.print("* "); pw.println(this); + pw.println(prefix + " mBounds=" + getRequestedOverrideBounds()); + if (dumpAll) { + printThisActivity(pw, mLastPausedActivity, dumpPackage, false, + prefix + " mLastPausedActivity: ", null); + } + } + + @Override + void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(HASH_CODE, System.identityHashCode(this)); + final ActivityRecord topActivity = topRunningActivity(); + proto.write(USER_ID, topActivity != null ? topActivity.mUserId : USER_NULL); + proto.write(TITLE, topActivity != null ? topActivity.intent.getComponent() + .flattenToShortString() : "TaskFragment"); + proto.end(token); + } + + @Override + long getProtoFieldId() { + return TASK_FRAGMENT; + } + + @Override + public void dumpDebug(ProtoOutputStream proto, long fieldId, + @WindowTraceLogLevel int logLevel) { + if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) { + return; + } + + final long token = proto.start(fieldId); + + super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); + + proto.write(DISPLAY_ID, getDisplayId()); + proto.write(ACTIVITY_TYPE, getActivityType()); + proto.write(MIN_WIDTH, mMinWidth); + proto.write(MIN_HEIGHT, mMinHeight); + + proto.end(token); + } +} diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java new file mode 100644 index 000000000000..31175b7e976c --- /dev/null +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; +import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; + +import android.content.res.Configuration; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.view.SurfaceControl; +import android.window.ITaskFragmentOrganizer; +import android.window.ITaskFragmentOrganizerController; +import android.window.TaskFragmentAppearedInfo; +import android.window.TaskFragmentInfo; + +import com.android.internal.protolog.common.ProtoLog; + +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * Stores and manages the client {@link android.window.TaskFragmentOrganizer}. + */ +public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub { + private static final String TAG = "TaskFragmentOrganizerController"; + + private final ActivityTaskManagerService mAtmService; + private final WindowManagerGlobalLock mGlobalLock; + private final Set<ITaskFragmentOrganizer> mOrganizers = new ArraySet<>(); + private final Map<ITaskFragmentOrganizer, DeathRecipient> mDeathRecipients = new ArrayMap<>(); + private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos = + new WeakHashMap<>(); + private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs = + new WeakHashMap<>(); + + private class DeathRecipient implements IBinder.DeathRecipient { + final ITaskFragmentOrganizer mOrganizer; + + DeathRecipient(ITaskFragmentOrganizer organizer) { + mOrganizer = organizer; + } + + @Override + public void binderDied() { + removeOrganizer(mOrganizer); + } + } + + TaskFragmentOrganizerController(ActivityTaskManagerService atm) { + mAtmService = atm; + mGlobalLock = atm.mGlobalLock; + } + + @Override + public void registerOrganizer(ITaskFragmentOrganizer organizer) { + final int pid = Binder.getCallingPid(); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Register task fragment organizer=%s uid=%d pid=%d", + organizer.asBinder(), uid, pid); + if (mOrganizers.contains(organizer)) { + throw new IllegalStateException( + "Replacing existing organizer currently unsupported"); + } + + final DeathRecipient dr = new DeathRecipient(organizer); + try { + organizer.asBinder().linkToDeath(dr, 0); + } catch (RemoteException e) { + // Oh well... + } + + mOrganizers.add(organizer); + mDeathRecipients.put(organizer, dr); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void unregisterOrganizer(ITaskFragmentOrganizer organizer) { + final int pid = Binder.getCallingPid(); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Unregister task fragment organizer=%s uid=%d pid=%d", + organizer.asBinder(), uid, pid); + if (!mOrganizers.contains(organizer)) { + throw new IllegalStateException( + "The task fragment organizer hasn't been registered."); + } + + final DeathRecipient dr = mDeathRecipients.get(organizer); + organizer.asBinder().unlinkToDeath(dr, 0); + + removeOrganizer(organizer); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) { + validateOrganizer(organizer); + + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName()); + final TaskFragmentInfo info = tf.getTaskFragmentInfo(); + final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(), + "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared"); + try { + organizer.onTaskFragmentAppeared( + new TaskFragmentAppearedInfo(info, outSurfaceControl)); + mLastSentTaskFragmentInfos.put(tf, info); + } catch (RemoteException e) { + // Oh well... + } + } + + void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) { + validateOrganizer(organizer); + + // Check if the info is different from the last reported info. + final TaskFragmentInfo info = tf.getTaskFragmentInfo(); + final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf); + if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer( + info.getConfiguration(), lastInfo.getConfiguration())) { + return; + } + + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s", tf.getName()); + try { + organizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo()); + mLastSentTaskFragmentInfos.put(tf, info); + } catch (RemoteException e) { + // Oh well... + } + } + + void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) { + validateOrganizer(organizer); + + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName()); + try { + organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo()); + } catch (RemoteException e) { + // Oh well... + } + mLastSentTaskFragmentInfos.remove(tf); + mLastSentTaskFragmentParentConfigs.remove(tf); + } + + void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) { + validateOrganizer(organizer); + + // Check if the parent info is different from the last reported parent info. + if (tf.getParent() == null || tf.getParent().asTask() == null) { + mLastSentTaskFragmentParentConfigs.remove(tf); + return; + } + final Task parent = tf.getParent().asTask(); + final Configuration parentConfig = parent.getConfiguration(); + final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf); + if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)) { + return; + } + + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "TaskFragment parent info changed name=%s parentTaskId=%d", + tf.getName(), parent.mTaskId); + try { + organizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig); + mLastSentTaskFragmentParentConfigs.put(tf, parentConfig); + } catch (RemoteException e) { + // Oh well... + } + } + + private void removeOrganizer(ITaskFragmentOrganizer organizer) { + synchronized (mGlobalLock) { + mOrganizers.remove(organizer); + mDeathRecipients.remove(organizer); + } + // TODO(b/190432728) move child activities of organized TaskFragment to leaf Task + } + + /** + * Makes sure that the organizer has been correctly registered to prevent any Sidecar + * implementation from organizing {@link TaskFragment} without registering first. In such case, + * we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the + * {@link TaskFragment} after the organizer process died. + */ + private void validateOrganizer(ITaskFragmentOrganizer organizer) { + if (!mOrganizers.contains(organizer)) { + throw new IllegalArgumentException( + "TaskFragmentOrganizer has not been registered. Organizer=" + organizer); + } + } +} diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index abcb34c2e8d9..a2bdad8a64fb 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -23,15 +23,13 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL; -import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS; -import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS; +import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.os.Binder; @@ -69,13 +67,6 @@ import java.util.function.Consumer; class TaskOrganizerController extends ITaskOrganizerController.Stub { private static final String TAG = "TaskOrganizerController"; - /** - * Masks specifying which configurations are important to report back to an organizer when - * changed. - */ - private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS; - private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS; - // The set of modes that are currently supports // TODO: Remove once the task organizer can support all modes @VisibleForTesting @@ -389,6 +380,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.mTaskOrganizer, t); } } + if (mService.getTransitionController().isShellTransitionsEnabled()) { + // dispose is only called outside of transitions (eg during unregister). Since + // we "migrate" surfaces when replacing organizers, visibility gets delegated + // to transitions; however, since there is no transition at this point, we have + // to manually show the surface here. + if (t.mTaskOrganizer != null && t.getSurfaceControl() != null) { + t.getSyncTransaction().show(t.getSurfaceControl()); + } + } } // Remove organizer state after removing tasks so we get a chance to send @@ -481,7 +481,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { - synchronized (mGlobalLock) { + final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>(); + final Runnable withGlobalLock = () -> { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d", organizer.asBinder(), uid); if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) { @@ -490,10 +491,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { new TaskOrganizerState(organizer, uid)); } - final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>(); final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); mService.mRootWindowContainer.forAllTasks((task) -> { - if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) { + if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, + task.getWindowingMode())) { return; } @@ -503,11 +504,19 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (returnTask) { SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task, "TaskOrganizerController.registerTaskOrganizer"); - taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl)); + taskInfos.add( + new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl)); } }); - return new ParceledListSlice<>(taskInfos); + }; + if (mService.getTransitionController().isShellTransitionsEnabled()) { + mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock); + } else { + synchronized (mGlobalLock) { + withGlobalLock.run(); + } } + return new ParceledListSlice<>(taskInfos); } finally { Binder.restoreCallingIdentity(origId); } @@ -519,7 +528,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { - synchronized (mGlobalLock) { + final Runnable withGlobalLock = () -> { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); if (state == null) { return; @@ -528,6 +537,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { organizer.asBinder(), uid); state.unlinkDeath(); state.dispose(); + }; + if (mService.getTransitionController().isShellTransitionsEnabled()) { + mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock); + } else { + synchronized (mGlobalLock) { + withGlobalLock.run(); + } } } finally { Binder.restoreCallingIdentity(origId); @@ -766,18 +782,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mTmpTaskInfo.configuration.unset(); task.fillTaskInfo(mTmpTaskInfo); - boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo); - if (!changed) { - int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration); - final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 - ? (int) mTmpTaskInfo.configuration.windowConfiguration.diff( - lastInfo.configuration.windowConfiguration, - true /* compareUndefined */) : 0; - if ((winCfgChanges & REPORT_WINDOW_CONFIGS) == 0) { - cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION; - } - changed = (cfgChanges & REPORT_CONFIGS) != 0; - } + boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo) + || !configurationsAreEqualForOrganizer( + mTmpTaskInfo.configuration, lastInfo.configuration); if (!(changed || force)) { // mTmpTaskInfo will be reused next time. return; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 0cd098070401..8e18da14d3b3 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -16,11 +16,13 @@ package com.android.server.wm; - +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; @@ -124,9 +126,16 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe /** The final animation targets derived from participants after promotion. */ private ArraySet<WindowContainer> mTargets = null; + private TransitionInfo.AnimationOptions mOverrideOptions; + private @TransitionState int mState = STATE_COLLECTING; private boolean mReadyCalled = false; + // TODO(b/188595497): remove when not needed. + /** @see RecentsAnimationController#mNavigationBarAttachedToApp */ + private boolean mNavBarAttachedToApp = false; + private int mNavBarDisplayId = INVALID_DISPLAY; + Transition(@WindowManager.TransitionType int type, @WindowManager.TransitionFlags int flags, TransitionController controller, BLASTSyncEngine syncEngine) { mType = type; @@ -136,6 +145,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mSyncId = mSyncEngine.startSyncSet(this); } + void addFlag(int flag) { + mFlags |= flag; + } + @VisibleForTesting int getSyncId() { return mSyncId; @@ -207,6 +220,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } /** + * Set animation options for collecting transition by ActivityRecord. + * @param options AnimationOptions captured from ActivityOptions + */ + void setOverrideAnimation(TransitionInfo.AnimationOptions options) { + if (mSyncId < 0) return; + mOverrideOptions = options; + } + + /** * Call this when all known changes related to this transition have been applied. Until * all participants have finished drawing, the transition can still collect participants. * @@ -275,12 +297,28 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } // Commit all going-invisible containers + boolean activitiesWentInvisible = false; for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); - if (ar != null && !ar.isVisibleRequested()) { - ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, - " Commit activity becoming invisible: %s", ar); - ar.commitVisibility(false /* visible */, false /* performLayout */); + if (ar != null) { + if (!ar.isVisibleRequested()) { + // If activity is capable of entering PiP, give it a chance to enter it now. + if (ar.getDeferHidingClient() && ar.getTask() != null) { + mController.mAtm.mTaskSupervisor.mUserLeaving = true; + ar.getTaskFragment().startPausing(false /* uiSleeping */, + null /* resuming */, "finishTransition"); + mController.mAtm.mTaskSupervisor.mUserLeaving = false; + } + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + " Commit activity becoming invisible: %s", ar); + ar.commitVisibility(false /* visible */, false /* performLayout */); + activitiesWentInvisible = true; + } + if (mChanges.get(ar).mVisible != ar.isVisibleRequested()) { + // Legacy dispatch relies on this (for now). + ar.mEnteringAnimation = ar.isVisibleRequested(); + } + mController.dispatchLegacyAppTransitionFinished(ar); } final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken(); if (wt != null && !wt.isVisibleRequested()) { @@ -289,6 +327,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe wt.commitVisibility(false /* visible */); } } + if (activitiesWentInvisible) { + // Always schedule stop processing when transition finishes because activities don't + // stop while they are in a transition thus their stop could still be pending. + mController.mAtm.mTaskSupervisor + .scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); + } + + legacyRestoreNavigationBarFromApp(); } void abort() { @@ -297,6 +343,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (mState != STATE_COLLECTING) { throw new IllegalStateException("Too late to abort."); } + mController.dispatchLegacyAppTransitionCancelled(); mState = STATE_ABORT; // Syncengine abort will call through to onTransactionReady() mSyncEngine.abort(mSyncId); @@ -327,6 +374,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mController.mAtm.mRootWindowContainer.getDisplayContent(displayId) .getPendingTransaction().merge(transaction); mSyncId = -1; + mOverrideOptions = null; return; } @@ -340,6 +388,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Resolve the animating targets from the participants mTargets = calculateTargets(mParticipants, mChanges); final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges); + info.setAnimationOptions(mOverrideOptions); + + // TODO(b/188669821): Move to animation impl in shell. + handleLegacyRecentsStartBehavior(displayId, info); handleNonAppWindowsInTransition(displayId, mType, mFlags); @@ -360,6 +412,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get(); buildFinishTransaction(mFinishTransaction, info.getRootLeash()); if (mController.getTransitionPlayer() != null) { + mController.dispatchLegacyAppTransitionStarting(info); try { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Calling onTransitionReady: %s", info); @@ -375,6 +428,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe cleanUpOnFailure(); } mSyncId = -1; + mOverrideOptions = null; } /** @@ -394,6 +448,100 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe finishTransition(); } + /** @see RecentsAnimationController#attachNavigationBarToApp */ + private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) { + if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) { + return; + } + final DisplayContent dc = + mController.mAtm.mRootWindowContainer.getDisplayContent(displayId); + if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() + // Skip the case where the nav bar is controlled by fade rotation. + || dc.getFadeRotationAnimationController() != null) { + return; + } + + WindowContainer topWC = null; + // Find the top-most non-home, closing app. + for (int i = 0; i < info.getChanges().size(); ++i) { + final TransitionInfo.Change c = info.getChanges().get(i); + if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId + || c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD + || !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) { + continue; + } + topWC = WindowContainer.fromBinder(c.getContainer().asBinder()); + break; + } + if (topWC == null || topWC.inMultiWindowMode()) { + return; + } + + final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar(); + if (navWindow == null || navWindow.mToken == null) { + return; + } + mNavBarAttachedToApp = true; + mNavBarDisplayId = displayId; + navWindow.mToken.cancelAnimation(); + final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); + final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); + t.reparent(navSurfaceControl, topWC.getSurfaceControl()); + t.show(navSurfaceControl); + + final WindowContainer imeContainer = dc.getImeContainer(); + if (imeContainer.isVisible()) { + t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1); + } else { + // Place the nav bar on top of anything else in the top activity. + t.setLayer(navSurfaceControl, Integer.MAX_VALUE); + } + if (mController.mStatusBar != null) { + mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false); + } + } + + /** @see RecentsAnimationController#restoreNavigationBarFromApp */ + void legacyRestoreNavigationBarFromApp() { + if (!mNavBarAttachedToApp) return; + mNavBarAttachedToApp = false; + + if (mController.mStatusBar != null) { + mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true); + } + + final DisplayContent dc = + mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId); + final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar(); + if (navWindow == null) return; + navWindow.setSurfaceTranslationY(0); + + final WindowToken navToken = navWindow.mToken; + if (navToken == null) return; + final SurfaceControl.Transaction t = dc.getPendingTransaction(); + final WindowContainer parent = navToken.getParent(); + t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer()); + + boolean animate = false; + // Search for the home task. If it is supposed to be visible, then the navbar is not at + // the bottom of the screen, so we need to animate it. + for (int i = 0; i < mTargets.size(); ++i) { + final Task task = mTargets.valueAt(i).asTask(); + if (task == null || !task.isHomeOrRecentsRootTask()) continue; + animate = task.isVisibleRequested(); + break; + } + + if (animate) { + final NavBarFadeAnimationController controller = + new NavBarFadeAnimationController(dc); + controller.fadeWindowToken(true); + } else { + // Reparent the SurfaceControl of nav bar token back. + t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); + } + } + private void handleNonAppWindowsInTransition(int displayId, @WindowManager.TransitionType int transit, int flags) { final DisplayContent dc = diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index cc63c4922c32..3186aeac0df8 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -17,6 +17,12 @@ package com.android.server.wm; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OPEN; import android.annotation.NonNull; @@ -24,14 +30,18 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Slog; import android.view.WindowManager; import android.window.IRemoteTransition; import android.window.ITransitionPlayer; +import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; +import com.android.server.LocalServices; +import com.android.server.statusbar.StatusBarManagerInternal; import java.util.ArrayList; @@ -44,12 +54,17 @@ class TransitionController { private ITransitionPlayer mTransitionPlayer; final ActivityTaskManagerService mAtm; + private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners = + new ArrayList<>(); + /** * Currently playing transitions (in the order they were started). When finished, records are * removed from this list. */ private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>(); + final Lock mRunningLock = new Lock(); + private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> { // clean-up/finish any playing transitions. for (int i = 0; i < mPlayingTransitions.size(); ++i) { @@ -57,13 +72,18 @@ class TransitionController { } mPlayingTransitions.clear(); mTransitionPlayer = null; + mRunningLock.doNotifyLocked(); }; /** The transition currently being constructed (collecting participants). */ private Transition mCollectingTransition = null; + // TODO(b/188595497): remove when not needed. + final StatusBarManagerInternal mStatusBar; + TransitionController(ActivityTaskManagerService atm) { mAtm = atm; + mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); } /** @see #createTransition(int, int) */ @@ -87,6 +107,7 @@ class TransitionController { mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", mCollectingTransition); + dispatchLegacyAppTransitionPending(); return mCollectingTransition; } @@ -240,6 +261,12 @@ class TransitionController { mCollectingTransition.collectExistenceChange(wc); } + /** @see Transition#setOverrideAnimation */ + void setOverrideAnimation(TransitionInfo.AnimationOptions options) { + if (mCollectingTransition == null) return; + mCollectingTransition.setOverrideAnimation(options); + } + /** @see Transition#setReady */ void setReady(boolean ready) { if (mCollectingTransition == null) return; @@ -261,6 +288,7 @@ class TransitionController { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record); mPlayingTransitions.remove(record); record.finishTransition(); + mRunningLock.doNotifyLocked(); } void moveToPlaying(Transition transition) { @@ -279,4 +307,97 @@ class TransitionController { mCollectingTransition = null; } + /** + * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy + * behaviors. + * TODO(b/188669821): Remove once legacy recents behavior is moved to shell. + */ + void setIsLegacyRecents() { + if (mCollectingTransition == null) return; + mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS); + } + + void legacyDetachNavigationBarFromApp(@NonNull IBinder token) { + final Transition transition = Transition.fromBinder(token); + if (transition == null || !mPlayingTransitions.contains(transition)) { + Slog.e(TAG, "Transition isn't playing: " + token); + return; + } + transition.legacyRestoreNavigationBarFromApp(); + } + + void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) { + mLegacyListeners.add(listener); + } + + void dispatchLegacyAppTransitionPending() { + for (int i = 0; i < mLegacyListeners.size(); ++i) { + mLegacyListeners.get(i).onAppTransitionPendingLocked(); + } + } + + void dispatchLegacyAppTransitionStarting(TransitionInfo info) { + final boolean keyguardGoingAway = info.getType() == TRANSIT_KEYGUARD_GOING_AWAY + || (info.getFlags() & (TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE + | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION + | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER + | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION)) != 0; + for (int i = 0; i < mLegacyListeners.size(); ++i) { + mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway, + 0 /* durationHint */, SystemClock.uptimeMillis(), + AnimationAdapter.STATUS_BAR_TRANSITION_DURATION); + } + } + + void dispatchLegacyAppTransitionFinished(ActivityRecord ar) { + for (int i = 0; i < mLegacyListeners.size(); ++i) { + mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token); + } + } + + void dispatchLegacyAppTransitionCancelled() { + for (int i = 0; i < mLegacyListeners.size(); ++i) { + mLegacyListeners.get(i).onAppTransitionCancelledLocked( + false /* keyguardGoingAway */); + } + } + + class Lock { + private int mTransitionWaiters = 0; + void runWhenIdle(long timeout, Runnable r) { + synchronized (mAtm.mGlobalLock) { + if (!inTransition()) { + r.run(); + return; + } + mTransitionWaiters += 1; + } + final long startTime = SystemClock.uptimeMillis(); + final long endTime = startTime + timeout; + while (true) { + synchronized (mAtm.mGlobalLock) { + if (!inTransition() || SystemClock.uptimeMillis() > endTime) { + mTransitionWaiters -= 1; + r.run(); + return; + } + } + synchronized (this) { + try { + this.wait(timeout); + } catch (InterruptedException e) { + return; + } + } + } + } + + void doNotifyLocked() { + synchronized (this) { + if (mTransitionWaiters > 0) { + this.notifyAll(); + } + } + } + } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index b1c7e196b70c..8b8123e39985 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1671,6 +1671,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return false; } + boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + if (mChildren.get(i).forAllLeafTaskFragments(callback)) { + return true; + } + } + return false; + } + /** * For all root tasks at or below this container call the callback. * @@ -1726,6 +1735,28 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } + /** + * For all task fragments at or below this container call the callback. + * + * @param callback Callback to be called for every task. + */ + void forAllTaskFragments(Consumer<TaskFragment> callback) { + forAllTaskFragments(callback, true /*traverseTopToBottom*/); + } + + void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) { + final int count = mChildren.size(); + if (traverseTopToBottom) { + for (int i = count - 1; i >= 0; --i) { + mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom); + } + } else { + for (int i = 0; i < count; i++) { + mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom); + } + } + } + void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) { final int count = mChildren.size(); if (traverseTopToBottom) { @@ -1739,6 +1770,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } + void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) { + final int count = mChildren.size(); + if (traverseTopToBottom) { + for (int i = count - 1; i >= 0; --i) { + mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom); + } + } else { + for (int i = 0; i < count; i++) { + mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom); + } + } + } + /** * For all root tasks at or below this container call the callback. * @@ -3075,6 +3119,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** Cheap way of doing cast and instanceof. */ + TaskFragment asTaskFragment() { + return null; + } + + /** Cheap way of doing cast and instanceof. */ WindowToken asWindowToken() { return null; } diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index ffd6d21c1026..baea85439582 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -89,6 +89,11 @@ public class WindowFrames { final Rect mCompatFrame = new Rect(); /** + * {@code true} if the window frame is a simulated frame and attached to a decor window. + */ + boolean mIsSimulatingDecorWindow = false; + + /** * Whether the parent frame would have been different if there was no display cutout. */ private boolean mParentFrameWasClippedByDisplayCutout; diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 47087cfbd147..5bc4d4997f17 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -40,6 +40,7 @@ import com.android.server.input.InputManagerService; import com.android.server.policy.WindowManagerPolicy; import java.util.List; +import java.util.Set; /** * Window manager local system service interface. @@ -54,17 +55,18 @@ public abstract class WindowManagerInternal { */ public interface AccessibilityControllerInternal { /** - * Enable the accessibility trace logging. + * Start tracing for the given logging types. + * @param loggingTypeFlags flags of the logging types enabled. */ - void startTrace(); + void startTrace(long loggingTypeFlags); /** - * Disable the accessibility trace logging. + * Disable accessibility tracing for all logging types. */ void stopTrace(); /** - * Is trace enabled or not. + * Is tracing enabled for any logging type. */ boolean isAccessibilityTracingEnabled(); @@ -73,20 +75,23 @@ public abstract class WindowManagerInternal { * * @param where A string to identify this log entry, which can be used to filter/search * through the tracing file. + * @param loggingTypeFlags The flags for the logging types this log entry belongs to. * @param callingParams The parameters for the method to be logged. * @param a11yDump The proto byte array for a11y state when the entry is generated. * @param callingUid The calling uid. * @param stackTrace The stack trace, null if not needed. + * @param ignoreStackEntries The stack entries can be removed */ void logTrace( - String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] stackTrace); + String where, long loggingTypeFlags, String callingParams, byte[] a11yDump, + int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries); /** * Add an accessibility trace entry. * * @param where A string to identify this log entry, which can be used to filter/search * through the tracing file. + * @param loggingTypeFlags The flags for the logging types this log entry belongs to. * @param callingParams The parameters for the method to be logged. * @param a11yDump The proto byte array for a11y state when the entry is generated. * @param callingUid The calling uid. @@ -94,9 +99,11 @@ public abstract class WindowManagerInternal { * @param timeStamp The time when the method to be logged is called. * @param processId The calling process Id. * @param threadId The calling thread Id. + * @param ignoreStackEntries The stack entries can be removed */ - void logTrace(String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] callStack, long timeStamp, int processId, long threadId); + void logTrace(String where, long loggingTypeFlags, String callingParams, + byte[] a11yDump, int callingUid, StackTraceElement[] callStack, long timeStamp, + int processId, long threadId, Set<String> ignoreStackEntries); } /** @@ -115,6 +122,16 @@ public abstract class WindowManagerInternal { */ void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows); + + /** + * Called when the display is reparented and becomes an embedded + * display. The {@link WindowsForAccessibilityCallback} with the given embedded + * display will be replaced by the {@link WindowsForAccessibilityCallback} + * associated with its parent display at the same time. + * + * @param embeddedDisplayId The embedded display Id. + */ + void onDisplayReparented(int embeddedDisplayId); } /** @@ -143,11 +160,11 @@ public abstract class WindowManagerInternal { void onRectangleOnScreenRequested(int left, int top, int right, int bottom); /** - * Notifies that the rotation changed. + * Notifies that the display size is changed when rotation or the + * logical display is changed. * - * @param rotation The current rotation. */ - void onRotationChanged(int rotation); + void onDisplaySizeChanged(); /** * Notifies that the context of the user changed. For example, an application diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 1ec9187d7a76..43a016d22fa5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -47,6 +47,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_P import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -1804,7 +1805,8 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mEnterAnimationPending = true; winAnimator.mEnteringAnimation = true; // Check if we need to prepare a transition for replacing window first. - if (activity != null && activity.isVisible() + if (mAtmService.getTransitionController().getTransitionPlayer() == null + && activity != null && activity.isVisible() && !prepareWindowReplacementTransition(activity)) { // If not, check if need to set up a dummy transition during display freeze // so that the unfreeze wait for the apps to draw. This might be needed if @@ -2478,7 +2480,7 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mActivityRecord != null) { win.mActivityRecord.updateReportedVisibilityLocked(); } - if (displayPolicy.areSystemBarsForcedShownLw(win)) { + if (displayPolicy.areSystemBarsForcedShownLw()) { result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS; } if (!win.isGoneForLayout()) { @@ -2524,7 +2526,8 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } if (winAnimator.mSurfaceController != null) { - win.calculateSurfaceBounds(win.getAttrs(), mTmpRect); + win.calculateSurfaceBounds(win.getLayoutingAttrs( + win.getWindowConfiguration().getRotation()), mTmpRect); outSurfaceSize.set(mTmpRect.width(), mTmpRect.height()); } getInsetsSourceControls(win, outActiveControls); @@ -5390,6 +5393,25 @@ public class WindowManagerService extends IWindowManager.Stub } } + void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) { + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setSandboxDisplayApis(sandboxDisplayApis); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + /** The global settings only apply to default display. */ private boolean applyForcedPropertiesForDefaultDisplay() { boolean changed = false; @@ -7571,6 +7593,7 @@ public class WindowManagerService extends IWindowManager.Stub public void registerAppTransitionListener(AppTransitionListener listener) { synchronized (mGlobalLock) { getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener); + mAtmService.getTransitionController().registerLegacyListener(listener); } } @@ -8556,7 +8579,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (win.mActivityRecord == null || !win.mActivityRecord.isState( - Task.ActivityState.RESUMED)) { + ActivityRecord.State.RESUMED)) { mDisplayHashController.sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW); return; @@ -8611,4 +8634,23 @@ public class WindowManagerService extends IWindowManager.Stub return snapshot != null && snapshot.hasImeSurface(); } } + + @Override + public int getImeDisplayId() { + // TODO(b/189805422): Add a toast to notify users that IMS may get extra + // onConfigurationChanged callback when perDisplayFocus is enabled. + // Enabling perDisplayFocus means that we track focus on each display, so we don't have + // the "top focus" display and getTopFocusedDisplayContent returns the default display + // as the fallback. It leads to InputMethodService receives an extra onConfiguration + // callback when InputMethodService move from a secondary display to another display + // with the same display metrics because InputMethodService will always associate with + // the ImeContainer on the default display in onCreate and receive a configuration update + // to match default display ImeContainer and then receive another configuration update + // from attachToWindowToken. + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getTopFocusedDisplayContent(); + return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId() + : DEFAULT_DISPLAY; + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index a94fd074ff2e..d59654949a27 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -19,6 +19,12 @@ package com.android.server.wm; import static android.os.Build.IS_USER; import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; + +import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.ParcelFileDescriptor; @@ -36,6 +42,7 @@ import com.android.internal.os.ByteTransferPipe; import com.android.internal.protolog.ProtoLogImpl; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; import java.io.IOException; import java.io.PrintWriter; @@ -58,10 +65,12 @@ public class WindowManagerShellCommand extends ShellCommand { // Internal service impl -- must perform security checks before touching. private final WindowManagerService mInternal; + private final LetterboxConfiguration mLetterboxConfiguration; public WindowManagerShellCommand(WindowManagerService service) { mInterface = service; mInternal = service; + mLetterboxConfiguration = service.mLetterboxConfiguration; } @Override @@ -113,6 +122,14 @@ public class WindowManagerShellCommand extends ShellCommand { return runGetIgnoreOrientationRequest(pw); case "dump-visible-window-views": return runDumpVisibleWindowViews(pw); + case "set-letterbox-style": + return runSetLetterboxStyle(pw); + case "get-letterbox-style": + return runGetLetterboxStyle(pw); + case "reset-letterbox-style": + return runResetLetterboxStyle(pw); + case "set-sandbox-display-apis": + return runSandboxDisplayApis(pw); case "set-multi-window-config": return runSetMultiWindowConfig(); case "get-multi-window-config": @@ -331,6 +348,37 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + /** + * Override display size and metrics to reflect the DisplayArea of the calling activity. + */ + private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException { + int displayId = Display.DEFAULT_DISPLAY; + String arg = getNextArgRequired(); + if ("-d".equals(arg)) { + displayId = Integer.parseInt(getNextArgRequired()); + arg = getNextArgRequired(); + } + + final boolean sandboxDisplayApis; + switch (arg) { + case "true": + case "1": + sandboxDisplayApis = true; + break; + case "false": + case "0": + sandboxDisplayApis = false; + break; + default: + getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we " + + "get " + arg); + return -1; + } + + mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis); + return 0; + } + private int runDismissKeyguard(PrintWriter pw) throws RemoteException { mInterface.dismissKeyguard(null /* callback */, null /* message */); return 0; @@ -548,6 +596,231 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException { + final float aspectRatio; + try { + String arg = getNextArgRequired(); + aspectRatio = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad aspect ratio format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or aspect ratio should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio); + } + return 0; + } + + private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException { + final int cornersRadius; + try { + String arg = getNextArgRequired(); + cornersRadius = Integer.parseInt(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad corners radius format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or corners radius should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius); + } + return 0; + } + + private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException { + @LetterboxBackgroundType final int backgroundType; + try { + String arg = getNextArgRequired(); + switch (arg) { + case "solid_color": + backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR; + break; + case "app_color_background": + backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; + break; + case "app_color_background_floating": + backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; + break; + case "wallpaper": + backgroundType = LETTERBOX_BACKGROUND_WALLPAPER; + break; + default: + getErrPrintWriter().println( + "Error: 'reset', 'solid_color', 'app_color_background' or " + + "'wallpaper' should be provided as an argument"); + return -1; + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset', 'solid_color', 'app_color_background' or " + + "'wallpaper' should be provided as an argument" + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType); + } + return 0; + } + + private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException { + final Color color; + try { + String arg = getNextArgRequired(); + color = Color.valueOf(Color.parseColor(arg)); + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or color in #RRGGBB format should be provided as " + + "an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundColor(color); + } + return 0; + } + + private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw) + throws RemoteException { + final int radius; + try { + String arg = getNextArgRequired(); + radius = Integer.parseInt(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: blur radius format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or blur radius should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius); + } + return 0; + } + + private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw) + throws RemoteException { + final float alpha; + try { + String arg = getNextArgRequired(); + alpha = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad alpha format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or alpha should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha); + } + return 0; + } + + private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException { + final float multiplier; + try { + String arg = getNextArgRequired(); + multiplier = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad multiplier format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or multiplier should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier); + } + return 0; + } + + private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException { + if (peekNextArg() == null) { + getErrPrintWriter().println("Error: No arguments provided."); + } + while (peekNextArg() != null) { + String arg = getNextArg(); + switch (arg) { + case "--aspectRatio": + runSetFixedOrientationLetterboxAspectRatio(pw); + break; + case "--cornerRadius": + runSetLetterboxActivityCornersRadius(pw); + break; + case "--backgroundType": + runSetLetterboxBackgroundType(pw); + break; + case "--backgroundColor": + runSetLetterboxBackgroundColor(pw); + break; + case "--wallpaperBlurRadius": + runSetLetterboxBackgroundWallpaperBlurRadius(pw); + break; + case "--wallpaperDarkScrimAlpha": + runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw); + break; + case "--horizontalPositionMultiplier": + runSeLetterboxHorizontalPositionMultiplier(pw); + break; + default: + getErrPrintWriter().println( + "Error: Unrecognized letterbox style option: " + arg); + return -1; + } + } + return 0; + } + + private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException { + if (peekNextArg() == null) { + resetLetterboxStyle(); + } + synchronized (mInternal.mGlobalLock) { + while (peekNextArg() != null) { + String arg = getNextArg(); + switch (arg) { + case "aspectRatio": + mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); + break; + case "cornerRadius": + mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); + break; + case "backgroundType": + mLetterboxConfiguration.resetLetterboxBackgroundType(); + break; + case "backgroundColor": + mLetterboxConfiguration.resetLetterboxBackgroundColor(); + break; + case "wallpaperBlurRadius": + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + break; + case "wallpaperDarkScrimAlpha": + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); + break; + case "horizontalPositionMultiplier": + mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); + break; + default: + getErrPrintWriter().println( + "Error: Unrecognized letterbox style option: " + arg); + return -1; + } + } + } + return 0; + } + private int runSetMultiWindowConfig() { if (peekNextArg() == null) { getErrPrintWriter().println("Error: No arguments provided."); @@ -622,6 +895,40 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private void resetLetterboxStyle() { + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); + mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundType(); + mLetterboxConfiguration.resetLetterboxBackgroundColor(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); + mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); + } + } + + private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException { + synchronized (mInternal.mGlobalLock) { + pw.println("Corner radius: " + + mLetterboxConfiguration.getLetterboxActivityCornersRadius()); + pw.println("Horizontal position multiplier: " + + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier()); + pw.println("Aspect ratio: " + + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio()); + + pw.println("Background type: " + + LetterboxConfiguration.letterboxBackgroundTypeToString( + mLetterboxConfiguration.getLetterboxBackgroundType())); + pw.println(" Background color: " + Integer.toHexString( + mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb())); + pw.println(" Wallpaper blur radius: " + + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); + pw.println(" Wallpaper dark scrim alpha: " + + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); + } + return 0; + } + private int runReset(PrintWriter pw) throws RemoteException { int displayId = getDisplayId(getNextArg()); @@ -646,6 +953,12 @@ public class WindowManagerShellCommand extends ShellCommand { // set-ignore-orientation-request mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */); + // set-letterbox-style + resetLetterboxStyle(); + + // set-sandbox-display-apis + mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true); + // set-multi-window-config runResetMultiWindowConfig(); @@ -680,7 +993,12 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]"); pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] "); pw.println(" If app requested orientation should be ignored."); + pw.println(" set-sandbox-display-apis [true|1|false|0]"); + pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect "); + pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or"); + pw.println(" Size Compat Mode."); + printLetterboxHelp(pw); printMultiWindowConfigHelp(pw); pw.println(" reset [-d DISPLAY_ID]"); @@ -693,6 +1011,49 @@ public class WindowManagerShellCommand extends ShellCommand { } } + private void printLetterboxHelp(PrintWriter pw) { + pw.println(" set-letterbox-style"); + pw.println(" Sets letterbox style using the following options:"); + pw.println(" --aspectRatio aspectRatio"); + pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= " + + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO); + pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will"); + pw.println(" be ignored and framework implementation will determine aspect ratio."); + pw.println(" --cornerRadius radius"); + pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,"); + pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be"); + pw.println(" ignored and corners of the activity won't be rounded."); + pw.println(" --backgroundType [reset|solid_color|app_color_background"); + pw.println(" |app_color_background_floating|wallpaper]"); + pw.println(" Type of background used in the letterbox mode."); + pw.println(" --backgroundColor color"); + pw.println(" Color of letterbox which is be used when letterbox background type"); + pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control"); + pw.println(" letterbox background type. See Color#parseColor for allowed color"); + pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive)."); + pw.println(" --wallpaperBlurRadius radius"); + pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0"); + pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius"); + pw.println(" are ignored and 0 is used."); + pw.println(" --wallpaperDarkScrimAlpha alpha"); + pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'"); + pw.println(" letterbox background. If alpha < 0 or >= 1 both it and"); + pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored"); + pw.println(" and 0.0 (transparent) is used instead."); + pw.println(" --horizontalPositionMultiplier multiplier"); + pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,"); + pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier"); + pw.println(" are ignored and central position (0.5) is used."); + pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); + pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); + pw.println(" |horizontalPositionMultiplier]"); + pw.println(" Resets overrides to default values for specified properties separated"); + pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); + pw.println(" If no arguments provided, all values will be reset."); + pw.println(" get-letterbox-style"); + pw.println(" Prints letterbox style configuration."); + } + private void printMultiWindowConfigHelp(PrintWriter pw) { pw.println(" set-multi-window-config"); pw.println(" Sets options to determine if activity should be shown in multi window:"); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index a82a478f2a4f..3d36cc517e98 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,12 +17,17 @@ package com.android.server.wm; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; @@ -34,6 +39,7 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; @@ -46,10 +52,12 @@ import android.util.ArraySet; import android.util.Slog; import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizerController; +import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; import android.window.IWindowOrganizerController; +import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; @@ -95,6 +103,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final TaskOrganizerController mTaskOrganizerController; final DisplayAreaOrganizerController mDisplayAreaOrganizerController; + final TaskFragmentOrganizerController mTaskFragmentOrganizerController; final TransitionController mTransitionController; @@ -103,6 +112,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mGlobalLock = atm.mGlobalLock; mTaskOrganizerController = new TaskOrganizerController(mService); mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService); + mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm); mTransitionController = new TransitionController(atm); } @@ -480,7 +490,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } else if (!task.mCreatedByOrganizer) { throw new UnsupportedOperationException( "Cannot set non-organized task as adjacent flag root: " + wc); - } else if (task.mAdjacentTask == null) { + } else if (task.getAdjacentTaskFragment() == null) { throw new UnsupportedOperationException( "Cannot set non-adjacent task as adjacent flag root: " + wc); } @@ -501,13 +511,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } + final WindowContainer wc; + final IBinder fragmentToken; switch (type) { case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); break; case HIERARCHY_OP_TYPE_REORDER: case HIERARCHY_OP_TYPE_REPARENT: - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); + wc = WindowContainer.fromBinder(hop.getContainer()); if (wc == null || !wc.isAttached()) { Slog.e(TAG, "Attempt to operate on detached container: " + wc); break; @@ -543,6 +555,40 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); mService.startActivityFromRecents(taskId, launchOpts); break; + case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: + final TaskFragmentCreationParams taskFragmentCreationOptions = + hop.getTaskFragmentCreationOptions(); + // TODO(b/190433129) add actual implementation on WM Core + break; + case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT: + wc = WindowContainer.fromBinder(hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + break; + } + final TaskFragment taskFragment = wc.asTaskFragment(); + if (taskFragment == null || taskFragment.asTask() != null) { + throw new IllegalArgumentException( + "Can only delete organized TaskFragment, but not Task."); + } + // TODO(b/190433129) add actual implementation on WM Core + break; + case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: + fragmentToken = hop.getContainer(); + final Intent activityIntent = hop.getActivityIntent(); + final Bundle activityOptions = hop.getLaunchOptions(); + // TODO(b/190433129) add actual implementation on WM Core + break; + case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: + fragmentToken = hop.getNewParent(); + final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); + // TODO(b/190433129) add actual implementation on WM Core + break; + case HIERARCHY_OP_TYPE_REPARENT_CHILDREN: + final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer()); + final WindowContainer newParent = WindowContainer.fromBinder(hop.getNewParent()); + // TODO(b/190433129) add actual implementation on WM Core + break; } return effects; } @@ -704,13 +750,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) { - final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask(); - final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask(); + final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment(); + final TaskFragment root2 = + WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment(); if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) { throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); } - root1.setAdjacentTask(root2); + root1.setAdjacentTaskFragment(root2); return TRANSACT_EFFECTS_LIFECYCLE; } @@ -747,6 +794,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return mDisplayAreaOrganizerController; } + @Override + public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() { + return mTaskFragmentOrganizerController; + } + @VisibleForTesting int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) { int id = mService.mWindowManager.mSyncEngine.startSyncSet(this); @@ -795,6 +847,22 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } + /** Whether the configuration changes are important to report back to an organizer. */ + static boolean configurationsAreEqualForOrganizer( + Configuration newConfig, @Nullable Configuration oldConfig) { + if (oldConfig == null) { + return false; + } + int cfgChanges = newConfig.diff(oldConfig); + final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0 + ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration, + true /* compareUndefined */) : 0; + if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) { + cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION; + } + return (cfgChanges & CONTROLLABLE_CONFIGS) == 0; + } + private void enforceTaskPermission(String func) { mService.enforceTaskPermission(func); } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 26cfbdf80681..bee722e8c5e6 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -25,6 +25,13 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.server.am.ActivityManagerService.MY_PID; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; +import static com.android.server.wm.ActivityRecord.State.DESTROYING; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STARTED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; @@ -32,13 +39,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; -import static com.android.server.wm.Task.ActivityState.DESTROYED; -import static com.android.server.wm.Task.ActivityState.DESTROYING; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.ActivityState.STARTED; -import static com.android.server.wm.Task.ActivityState.STOPPING; import android.Manifest; import android.annotation.NonNull; @@ -735,10 +735,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (canUpdate) { // Make sure the previous top activity in the process no longer be resumed. if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) { - final Task task = mPreQTopResumedActivity.getTask(); - if (task != null) { - boolean userLeaving = task.shouldBeVisible(null); - task.startPausingLocked(userLeaving, false /* uiSleeping */, + final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment(); + if (taskFrag != null) { + boolean userLeaving = taskFrag.shouldBeVisible(null); + taskFrag.startPausing(userLeaving, false /* uiSleeping */, activity, "top-resumed-changed"); } } @@ -989,7 +989,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Since there could be more than one activities in a process record, we don't need to // compute the OomAdj with each of them, just need to find out the activity with the // "best" state, the order would be visible, pausing, stopping... - Task.ActivityState bestInvisibleState = DESTROYED; + ActivityRecord.State bestInvisibleState = DESTROYED; boolean allStoppingFinishing = true; boolean visible = false; int minTaskLayer = Integer.MAX_VALUE; @@ -1213,12 +1213,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio hasVisibleActivities = true; } - final Task task = r.getTask(); - if (task != null) { + final TaskFragment taskFragment = r.getTaskFragment(); + if (taskFragment != null) { // There may be a pausing activity that hasn't shown any window and was requested // to be hidden. But pausing is also a visible state, it should be regarded as // visible, so the caller can know the next activity should be resumed. - hasVisibleActivities |= task.handleAppDied(this); + hasVisibleActivities |= taskFragment.handleAppDied(this); } r.handleAppDied(); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c3fc99554bcc..8ec6ed2908f8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -34,6 +34,7 @@ import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.SurfaceControl.Transaction; import static android.view.SurfaceControl.getGlobalTransaction; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; @@ -202,6 +203,7 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; +import android.gui.TouchOcclusionMode; import android.os.Binder; import android.os.Build; import android.os.Debug; @@ -211,7 +213,6 @@ import android.os.PowerManager.WakeReason; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; -import android.os.TouchOcclusionMode; import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; @@ -1259,8 +1260,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP frame.inset(left, top, right, bottom); } - void computeFrameAndUpdateSourceFrame() { - computeFrame(); + void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) { + computeFrame(displayFrames); // Update the source frame to provide insets to other windows during layout. If the // simulated frames exist, then this is not computing a stable result so just skip. if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) { @@ -1271,7 +1272,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * Perform standard frame computation. The result can be obtained with getFrame() if so desired. */ - void computeFrame() { + void computeFrame(DisplayFrames displayFrames) { if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) { // This window is being replaced and either already got information that it's being // removed or we are still waiting for some information. Because of this we don't @@ -1384,7 +1385,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final int fw = windowFrames.mFrame.width(); final int fh = windowFrames.mFrame.height(); - applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame); + applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame, + displayFrames); if (mAttrs.type == TYPE_DOCK_DIVIDER) { if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) { @@ -1477,6 +1479,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mAttrs; } + WindowManager.LayoutParams getLayoutingAttrs(int rotation) { + if (!INSETS_LAYOUT_GENERALIZATION) { + return mAttrs; + } + final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation; + if (paramsForRotation == null || paramsForRotation.length != 4 + || paramsForRotation[rotation] == null) { + return mAttrs; + } + return paramsForRotation[rotation]; + } + /** Retrieves the flags used to disable system UI functions. */ int getDisableFlags() { return mDisableFlags; @@ -1842,9 +1856,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return super.hasContentToDisplay(); } - @Override - boolean isVisible() { - return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy() + private boolean isVisibleByPolicyOrInsets() { + return isVisibleByPolicy() // If we don't have a provider, this window isn't used as a window generating // insets, so nobody can hide it over the inset APIs. && (mControllableInsetProvider == null @@ -1852,11 +1865,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } @Override + boolean isVisible() { + return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicyOrInsets(); + } + + @Override boolean isVisibleRequested() { - if (shouldCheckTokenVisibleRequested()) { - return isVisible() && mToken.isVisibleRequested(); + final boolean localVisibleRequested = + wouldBeVisibleRequestedIfPolicyIgnored() && isVisibleByPolicyOrInsets(); + if (localVisibleRequested && shouldCheckTokenVisibleRequested()) { + return mToken.isVisibleRequested(); } - return isVisible(); + return localVisibleRequested; } /** @@ -1903,6 +1923,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return !isWallpaper || mToken.isVisible(); } + private boolean wouldBeVisibleRequestedIfPolicyIgnored() { + final WindowState parent = getParentWindow(); + final boolean isParentHiddenRequested = parent != null && !parent.isVisibleRequested(); + if (isParentHiddenRequested || mAnimatingExit || mDestroying) { + return false; + } + final boolean isWallpaper = mToken.asWallpaperToken() != null; + return !isWallpaper || mToken.isVisibleRequested(); + } + /** * Is this window visible, ignoring its app token? It is not visible if there is no surface, * or we are in the process of running an exit animation that will remove the surface. @@ -3863,7 +3893,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final boolean forceRelayout = syncRedraw || reportOrientation || isDragResizeChanged(); final DisplayContent displayContent = getDisplayContent(); final boolean alwaysConsumeSystemBars = - displayContent.getDisplayPolicy().areSystemBarsForcedShownLw(this); + displayContent.getDisplayPolicy().areSystemBarsForcedShownLw(); final int displayId = displayContent.getDisplayId(); markRedrawForSyncReported(); @@ -4397,12 +4427,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame, - Rect displayFrame) { + Rect displayFrame, DisplayFrames displayFrames) { final int pw = containingFrame.width(); final int ph = containingFrame.height(); final Task task = getTask(); final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds(); - final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0; + final WindowManager.LayoutParams attrs = getLayoutingAttrs(displayFrames.mRotation); + final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0; // We need to fit it to the display if either // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen @@ -4412,49 +4443,54 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // screen, but SurfaceViews want to be always at a specific location so we don't fit it to // the display. final boolean fitToDisplay = (task == null || !inNonFullscreenContainer) - || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits); + || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits); float x, y; int w,h; final boolean hasCompatScale = hasCompatScale(); - if ((mAttrs.flags & FLAG_SCALED) != 0) { - if (mAttrs.width < 0) { + if ((attrs.flags & FLAG_SCALED) != 0 || mAttrs != attrs) { + // For the window with different layout attrs for different rotations, we need to avoid + // using requested size. Otherwise, when finishing a simulated rotation, the information + // coming from WindowManagerServices to the ViewRootImpl may not contain the correct + // value for the new rotation, and there will be a quick flash of wrong layout when the + // simulated activity faded out. + if (attrs.width < 0) { w = pw; } else if (hasCompatScale) { - w = (int)(mAttrs.width * mGlobalScale + .5f); + w = (int) (attrs.width * mGlobalScale + .5f); } else { - w = mAttrs.width; + w = attrs.width; } - if (mAttrs.height < 0) { + if (attrs.height < 0) { h = ph; } else if (hasCompatScale) { - h = (int)(mAttrs.height * mGlobalScale + .5f); + h = (int) (attrs.height * mGlobalScale + .5f); } else { - h = mAttrs.height; + h = attrs.height; } } else { - if (mAttrs.width == MATCH_PARENT) { + if (attrs.width == MATCH_PARENT) { w = pw; } else if (hasCompatScale) { - w = (int)(mRequestedWidth * mGlobalScale + .5f); + w = (int) (mRequestedWidth * mGlobalScale + .5f); } else { w = mRequestedWidth; } - if (mAttrs.height == MATCH_PARENT) { + if (attrs.height == MATCH_PARENT) { h = ph; } else if (hasCompatScale) { - h = (int)(mRequestedHeight * mGlobalScale + .5f); + h = (int) (mRequestedHeight * mGlobalScale + .5f); } else { h = mRequestedHeight; } } if (hasCompatScale) { - x = mAttrs.x * mGlobalScale; - y = mAttrs.y * mGlobalScale; + x = attrs.x * mGlobalScale; + y = attrs.y * mGlobalScale; } else { - x = mAttrs.x; - y = mAttrs.y; + x = attrs.x; + y = attrs.y; } if (inNonFullscreenContainer && !layoutInParentFrame()) { @@ -4481,13 +4517,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } // Set mFrame - Gravity.apply(mAttrs.gravity, w, h, containingFrame, - (int) (x + mAttrs.horizontalMargin * pw), - (int) (y + mAttrs.verticalMargin * ph), windowFrames.mFrame); - + Gravity.apply(attrs.gravity, w, h, containingFrame, + (int) (x + attrs.horizontalMargin * pw), + (int) (y + attrs.verticalMargin * ph), windowFrames.mFrame); // Now make sure the window fits in the overall display frame. if (fitToDisplay) { - Gravity.applyDisplay(mAttrs.gravity, displayFrame, windowFrames.mFrame); + Gravity.applyDisplay(attrs.gravity, displayFrame, windowFrames.mFrame); } // We need to make sure we update the CompatFrame as it is used for diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index fbfa400ba852..26241421cef9 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,9 +16,11 @@ package com.android.server.wm; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -451,9 +453,24 @@ class WindowToken extends WindowContainer<WindowState> { } Rect getFixedRotationBarContentFrame(int windowType) { - return isFixedRotationTransforming() - ? mFixedRotationTransformState.mBarContentFrames.get(windowType) - : null; + if (!isFixedRotationTransforming()) { + return null; + } + if (!INSETS_LAYOUT_GENERALIZATION) { + return mFixedRotationTransformState.mBarContentFrames.get(windowType); + } + final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames; + final Rect tmpRect = new Rect(); + if (windowType == TYPE_NAVIGATION_BAR) { + tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR) + .getFrame()); + } + if (windowType == TYPE_STATUS_BAR) { + tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR) + .getFrame()); + } + tmpRect.intersect(displayFrames.mDisplayCutoutSafe); + return tmpRect; } InsetsState getFixedRotationTransformInsetsState() { diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java index 64b24c12f046..43188f630729 100644 --- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java @@ -95,18 +95,25 @@ public final class TestableDeviceConfig implements StaticMockFixture { String name = invocationOnMock.getArgument(1); String value = invocationOnMock.getArgument(2); mKeyValueMap.put(getKey(namespace, name), value); - for (DeviceConfig.OnPropertiesChangedListener listener : - mOnPropertiesChangedListenerMap.keySet()) { - if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) { - mOnPropertiesChangedListenerMap.get(listener).second.execute( - () -> listener.onPropertiesChanged( - getProperties(namespace, name, value))); - } - } + invokeListeners(namespace, getProperties(namespace, name, value)); return true; } ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean())); + doAnswer((Answer<Boolean>) invocationOnMock -> { + Properties properties = invocationOnMock.getArgument(0); + String namespace = properties.getNamespace(); + Map<String, String> keyValues = new ArrayMap<>(); + for (String name : properties.getKeyset()) { + String value = properties.getString(name, /* defaultValue= */ ""); + mKeyValueMap.put(getKey(namespace, name), value); + keyValues.put(name.toLowerCase(), value); + } + invokeListeners(namespace, getProperties(namespace, keyValues)); + return true; + } + ).when(() -> DeviceConfig.setProperties(any(Properties.class))); + doAnswer((Answer<String>) invocationOnMock -> { String namespace = invocationOnMock.getArgument(0); String name = invocationOnMock.getArgument(1); @@ -153,6 +160,16 @@ public final class TestableDeviceConfig implements StaticMockFixture { return Pair.create(values[0], values[1]); } + private void invokeListeners(String namespace, Properties properties) { + for (DeviceConfig.OnPropertiesChangedListener listener : + mOnPropertiesChangedListenerMap.keySet()) { + if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) { + mOnPropertiesChangedListenerMap.get(listener).second.execute( + () -> listener.onPropertiesChanged(properties)); + } + } + } + private Properties getProperties(String namespace, String name, String value) { return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value)); } diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java index 0e40669cf870..d68b81490f6e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import android.app.ActivityThread; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.BadConfigException; import android.provider.DeviceConfig.Properties; import androidx.test.filters.SmallTest; @@ -92,6 +93,16 @@ public class TestableDeviceConfigTest { } @Test + public void setProperties() throws BadConfigException { + String newKey = "key2"; + String newValue = "value2"; + DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey, + sValue).setString(newKey, newValue).build()); + assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue); + assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue); + } + + @Test public void getProperties_empty() { String newKey = "key2"; String newValue = "value2"; @@ -131,13 +142,12 @@ public class TestableDeviceConfigTest { } @Test - public void testListener() throws InterruptedException { + public void testListener_setProperty() throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); OnPropertiesChangedListener changeListener = (properties) -> { assertThat(properties.getNamespace()).isEqualTo(sNamespace); - assertThat(properties.getKeyset().size()).isEqualTo(1); - assertThat(properties.getKeyset()).contains(sKey); + assertThat(properties.getKeyset()).containsExactly(sKey); assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue); assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value"); countDownLatch.countDown(); @@ -153,6 +163,32 @@ public class TestableDeviceConfigTest { } } + @Test + public void testListener_setProperties() throws BadConfigException, InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + String newKey = "key2"; + String newValue = "value2"; + + OnPropertiesChangedListener changeListener = (properties) -> { + assertThat(properties.getNamespace()).isEqualTo(sNamespace); + assertThat(properties.getKeyset()).containsExactly(sKey, newKey); + assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue); + assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue); + assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value"); + countDownLatch.countDown(); + }; + try { + DeviceConfig.addOnPropertiesChangedListener(sNamespace, + ActivityThread.currentApplication().getMainExecutor(), changeListener); + DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey, + sValue).setString(newKey, newValue).build()); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + } finally { + DeviceConfig.removeOnPropertiesChangedListener(changeListener); + } + } + } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index d6c11a549dfa..e612d121b093 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -65,6 +65,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; import android.content.ComponentName; import android.content.Context; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index 00daa5c89fba..432a500a5041 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.GestureDescription; import android.accessibilityservice.IAccessibilityServiceClient; import android.content.ComponentName; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java index e4d51e4374a7..4afe0996e12a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -120,6 +120,7 @@ public class AccessibilityWindowManagerTest { @Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; @Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; + @Mock private AccessibilityTraceManager mMockA11yTraceManager; @Mock private IBinder mMockHostToken; @Mock private IBinder mMockEmbeddedToken; @@ -140,7 +141,8 @@ public class AccessibilityWindowManagerTest { mMockWindowManagerInternal, mMockA11yEventSender, mMockA11ySecurityPolicy, - mMockA11yUserManager); + mMockA11yUserManager, + mMockA11yTraceManager); // Starts tracking window of default display and sets the default display // as top focused display before each testing starts. startTrackingPerDisplay(Display.DEFAULT_DISPLAY); @@ -834,6 +836,19 @@ public class AccessibilityWindowManagerTest { assertNull(token); } + @Test + public void onDisplayReparented_shouldRemoveObserver() throws RemoteException { + // Starts tracking window of second display. + startTrackingPerDisplay(SECONDARY_DISPLAY_ID); + assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID)); + // Notifies the second display is an embedded one of the default display. + final WindowsForAccessibilityCallback callbacks = + mCallbackOfWindows.get(Display.DEFAULT_DISPLAY); + callbacks.onDisplayReparented(SECONDARY_DISPLAY_ID); + // Makes sure the observer of the second display is removed. + assertFalse(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID)); + } + private void registerLeashedTokenAndWindowId() { mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID); mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java index 78e651b7a3c1..c62cae5e9b6e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java @@ -56,11 +56,13 @@ public class KeyboardInterceptorTest { private MessageCapturingHandler mHandler = new MessageCapturingHandler( msg -> mInterceptor.handleMessage(msg)); @Mock AccessibilityManagerService mMockAms; + @Mock AccessibilityTraceManager mMockTraceManager; @Mock WindowManagerPolicy mMockPolicy; @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager); mInterceptor = new KeyboardInterceptor(mMockAms, mMockPolicy, mHandler); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java index d4908ee78a1d..59b69f9ffebf 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java @@ -122,6 +122,7 @@ public class MotionEventInjectorTest { MotionEventInjector mMotionEventInjector; IAccessibilityServiceClient mServiceInterface; + AccessibilityTraceManager mTrace; List<GestureStep> mLineList = new ArrayList<>(); List<GestureStep> mClickList = new ArrayList<>(); List<GestureStep> mContinuedLineList1 = new ArrayList<>(); @@ -148,7 +149,8 @@ public class MotionEventInjectorTest { return mMotionEventInjector.handleMessage(msg); } }); - mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler); + mTrace = mock(AccessibilityTraceManager.class); + mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler, mTrace); mServiceInterface = mock(IAccessibilityServiceClient.class); mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java index 160308762a58..4ce9ba031b25 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; import android.app.UiAutomation; import android.content.Context; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 7bf0bb873fc3..8a745d511bbf 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -36,6 +36,7 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_E import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityGestureEvent; import android.accessibilityservice.AccessibilityService; @@ -53,6 +54,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.utils.GestureLogParser; import com.android.server.testutils.OffsettableClock; @@ -107,6 +109,8 @@ public class TouchExplorerTest { @Mock private AccessibilityManagerService mMockAms; + @Mock + private AccessibilityTraceManager mMockTraceManager; @Captor private ArgumentCaptor<AccessibilityGestureEvent> mGestureCaptor; @@ -143,6 +147,7 @@ public class TouchExplorerTest { if (Looper.myLooper() == null) { Looper.prepare(); } + when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager); mContext = InstrumentationRegistry.getContext(); mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); AccessibilityManagerService ams = new AccessibilityManagerService(mContext); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 502f64a1e50b..fe4fed9da468 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; @@ -93,6 +94,7 @@ public class FullScreenMagnificationControllerTest { mock(FullScreenMagnificationController.ControllerContext.class); final Context mMockContext = mock(Context.class); final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class); + final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class); final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class); private final MagnificationAnimationCallback mAnimationCallback = mock( MagnificationAnimationCallback.class); @@ -113,9 +115,11 @@ public class FullScreenMagnificationControllerTest { when(mMockContext.getMainLooper()).thenReturn(looper); when(mMockControllerCtx.getContext()).thenReturn(mMockContext); when(mMockControllerCtx.getAms()).thenReturn(mMockAms); + when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager); when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager); when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler); when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L); + when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager); initMockWindowManager(); mFullScreenMagnificationController = new FullScreenMagnificationController( @@ -773,20 +777,20 @@ public class FullScreenMagnificationControllerTest { } @Test - public void testRotation_resetsMagnification() { + public void testDisplaySizeChanged_resetsMagnification() { for (int i = 0; i < DISPLAY_COUNT; i++) { - rotation_resetsMagnification(i); + changeDisplaySize_resetsMagnification(i); resetMockWindowManager(); } } - private void rotation_resetsMagnification(int displayId) { + private void changeDisplaySize_resetsMagnification(int displayId) { register(displayId); MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId); zoomIn2xToMiddle(displayId); mMessageCapturingHandler.sendAllMessages(); assertTrue(mFullScreenMagnificationController.isMagnifying(displayId)); - callbacks.onRotationChanged(0); + callbacks.onDisplaySizeChanged(); mMessageCapturingHandler.sendAllMessages(); assertFalse(mFullScreenMagnificationController.isMagnifying(displayId)); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index f881f04e5b07..b14c353397e2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import com.android.server.testutils.OffsettableClock; @@ -129,6 +130,10 @@ public class FullScreenMagnificationGestureHandlerTest { MagnificationInfoChangedCallback mMagnificationInfoChangedCallback; @Mock WindowMagnificationPromptController mWindowMagnificationPromptController; + @Mock + AccessibilityManagerService mMockAccessibilityManagerService; + @Mock + AccessibilityTraceManager mMockTraceManager; private OffsettableClock mClock; private FullScreenMagnificationGestureHandler mMgh; @@ -144,7 +149,9 @@ public class FullScreenMagnificationGestureHandlerTest { mock(FullScreenMagnificationController.ControllerContext.class); final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class); when(mockController.getContext()).thenReturn(mContext); - when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class)); + when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService); + when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager); + when(mockController.getTraceManager()).thenReturn(mMockTraceManager); when(mockController.getWindowManager()).thenReturn(mockWindowManager); when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper())); when(mockController.newValueAnimator()).thenReturn(new ValueAnimator()); @@ -179,7 +186,7 @@ public class FullScreenMagnificationGestureHandlerTest { private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap, boolean detectShortcutTrigger) { FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler( - mContext, mFullScreenMagnificationController, mMockCallback, + mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback, detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController, DISPLAY_0); mHandler = new TestHandler(h.mDetectingState, mClock) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index b7f5f4da9d9d..e82adc8b403b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -54,6 +54,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.AccessibilityTraceManager; import org.junit.After; import org.junit.Before; @@ -84,6 +85,8 @@ public class MagnificationControllerTest { Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; @Mock + private AccessibilityTraceManager mTraceManager; + @Mock private AccessibilityManagerService mService; @Mock private MagnificationController.TransitionCallBack mTransitionCallBack; @@ -112,7 +115,7 @@ public class MagnificationControllerTest { CURRENT_USER_ID); mWindowMagnificationManager = Mockito.spy( new WindowMagnificationManager(mContext, CURRENT_USER_ID, - mock(WindowMagnificationManager.Callback.class))); + mock(WindowMagnificationManager.Callback.class), mTraceManager)); mMockConnection = new MockWindowMagnificationConnection(true); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber( diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java index 514d16a0149c..ef6ed88011ef 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java @@ -31,6 +31,8 @@ import android.view.MotionEvent; import androidx.test.runner.AndroidJUnit4; +import com.android.server.accessibility.AccessibilityTraceManager; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +51,8 @@ public class MagnificationGestureHandlerTest { Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; @Mock + AccessibilityTraceManager mTraceManager; + @Mock MagnificationGestureHandler.Callback mCallback; @Before @@ -57,6 +61,7 @@ public class MagnificationGestureHandlerTest { mMgh = new TestMagnificationGestureHandler(DISPLAY_0, /* detectTripleTap= */true, /* detectShortcutTrigger= */true, + mTraceManager, mCallback); } @@ -129,8 +134,9 @@ public class MagnificationGestureHandlerTest { boolean mIsInternalMethodCalled = false; TestMagnificationGestureHandler(int displayId, boolean detectTripleTap, - boolean detectShortcutTrigger, @NonNull Callback callback) { - super(displayId, detectTripleTap, detectShortcutTrigger, callback); + boolean detectShortcutTrigger, @NonNull AccessibilityTraceManager trace, + @NonNull Callback callback) { + super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback); } @Override diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java index c88bc3b2e15b..1638563e8242 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java @@ -29,6 +29,8 @@ import android.view.accessibility.IWindowMagnificationConnection; import android.view.accessibility.IWindowMagnificationConnectionCallback; import android.view.accessibility.MagnificationAnimationCallback; +import com.android.server.accessibility.AccessibilityTraceManager; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -45,6 +47,8 @@ public class WindowMagnificationConnectionWrapperTest { private IWindowMagnificationConnection mConnection; @Mock + private AccessibilityTraceManager mTrace; + @Mock private IWindowMagnificationConnectionCallback mCallback; @Mock private MagnificationAnimationCallback mAnimationCallback; @@ -57,7 +61,7 @@ public class WindowMagnificationConnectionWrapperTest { MockitoAnnotations.initMocks(this); mMockWindowMagnificationConnection = new MockWindowMagnificationConnection(); mConnection = mMockWindowMagnificationConnection.getConnection(); - mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection); + mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection, mTrace); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index b9498d641ed7..6a5aae672881 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -35,6 +35,7 @@ import android.view.ViewConfiguration; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.utils.TouchEventGenerator; @@ -74,16 +75,18 @@ public class WindowMagnificationGestureHandlerTest { private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler; @Mock MagnificationGestureHandler.Callback mMockCallback; + @Mock + AccessibilityTraceManager mMockTrace; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getInstrumentation().getContext(); mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0, - mock(WindowMagnificationManager.Callback.class)); + mock(WindowMagnificationManager.Callback.class), mMockTrace); mMockConnection = new MockWindowMagnificationConnection(); mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler( - mContext, mWindowMagnificationManager, mMockCallback, + mContext, mWindowMagnificationManager, mMockTrace, mMockCallback, /** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index a20272ab438d..af6d40f2fdf2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -52,6 +52,7 @@ import android.view.accessibility.MagnificationAnimationCallback; import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.LocalServices; +import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.Before; @@ -72,6 +73,8 @@ public class WindowMagnificationManagerTest { @Mock private Context mContext; @Mock + private AccessibilityTraceManager mMockTrace; + @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal; @Mock private MagnificationAnimationCallback mAnimationCallback; @@ -88,7 +91,7 @@ public class WindowMagnificationManagerTest { mResolver = new MockContentResolver(); mMockConnection = new MockWindowMagnificationConnection(); mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID, - mMockCallback); + mMockCallback, mMockTrace); when(mContext.getContentResolver()).thenReturn(mResolver); doAnswer((InvocationOnMock invocation) -> { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 32a4774ca4f2..697d1021889e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -260,7 +260,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { notifyActivityLaunching(mTopActivity.intent); notifyActivityLaunched(START_SUCCESS, mTopActivity); doReturn(true).when(mTopActivity.mDisplayContent).isSleeping(); - mTopActivity.setState(Task.ActivityState.RESUMED, "test"); + mTopActivity.setState(ActivityRecord.State.RESUMED, "test"); mTopActivity.setVisibility(false); waitHandlerIdle(mAtm.mH); // Not cancel immediately because in one of real cases, the keyguard may be going away or diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index b7713a9338de..02e0e6285b42 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -65,19 +65,19 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED; import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED; import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED; -import static com.android.server.wm.Task.ActivityState.DESTROYED; -import static com.android.server.wm.Task.ActivityState.DESTROYING; -import static com.android.server.wm.Task.ActivityState.FINISHING; -import static com.android.server.wm.Task.ActivityState.INITIALIZING; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.ActivityState.STARTED; -import static com.android.server.wm.Task.ActivityState.STOPPED; -import static com.android.server.wm.Task.ActivityState.STOPPING; -import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; +import static com.android.server.wm.ActivityRecord.State.DESTROYING; +import static com.android.server.wm.ActivityRecord.State.FINISHING; +import static com.android.server.wm.ActivityRecord.State.INITIALIZING; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STARTED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM; @@ -133,7 +133,7 @@ import android.window.TaskSnapshot; import androidx.test.filters.MediumTest; import com.android.internal.R; -import com.android.server.wm.Task.ActivityState; +import com.android.server.wm.ActivityRecord.State; import org.junit.Before; import org.junit.Test; @@ -167,25 +167,25 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test - public void testStackCleanupOnClearingTask() { + public void testTaskFragmentCleanupOnClearingTask() { final ActivityRecord activity = createActivityWith2LevelTask(); final Task task = activity.getTask(); - final Task rootTask = activity.getRootTask(); + final TaskFragment taskFragment = activity.getTaskFragment(); activity.onParentChanged(null /*newParent*/, task); - verify(rootTask, times(1)).cleanUpActivityReferences(any()); + verify(taskFragment).cleanUpActivityReferences(any()); } @Test - public void testStackCleanupOnActivityRemoval() { + public void testTaskFragmentCleanupOnActivityRemoval() { final ActivityRecord activity = createActivityWith2LevelTask(); final Task task = activity.getTask(); - final Task rootTask = activity.getRootTask(); + final TaskFragment taskFragment = activity.getTaskFragment(); task.removeChild(activity); - verify(rootTask, times(1)).cleanUpActivityReferences(any()); + verify(taskFragment).cleanUpActivityReferences(any()); } @Test - public void testStackCleanupOnTaskRemoval() { + public void testRootTaskCleanupOnTaskRemoval() { final ActivityRecord activity = createActivityWith2LevelTask(); final Task task = activity.getTask(); final Task rootTask = activity.getRootTask(); @@ -340,7 +340,7 @@ public class ActivityRecordTests extends WindowTestsBase { public void testSetsRelaunchReason_NotDragResizing() { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(RESUMED, "Testing"); task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), @@ -365,7 +365,7 @@ public class ActivityRecordTests extends WindowTestsBase { public void testSetsRelaunchReason_DragResizing() { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(RESUMED, "Testing"); task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), @@ -392,7 +392,7 @@ public class ActivityRecordTests extends WindowTestsBase { public void testRelaunchClearTopWaitingTranslucent() { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(RESUMED, "Testing"); task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), @@ -413,7 +413,7 @@ public class ActivityRecordTests extends WindowTestsBase { public void testSetsRelaunchReason_NonResizeConfigChanges() { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(RESUMED, "Testing"); task.onRequestedOverrideConfigurationChanged(task.getConfiguration()); activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), @@ -461,7 +461,7 @@ public class ActivityRecordTests extends WindowTestsBase { .setCreateTask(true) .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT) .build(); - activity.setState(Task.ActivityState.RESUMED, "Testing"); + activity.setState(RESUMED, "Testing"); activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), activity.getConfiguration())); @@ -624,7 +624,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testShouldMakeActive_deferredResume() { final ActivityRecord activity = createActivityWithTask(); - activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setState(STOPPED, "Testing"); mSupervisor.beginDeferResume(); assertEquals(false, activity.shouldMakeActive(null /* activeActivity */)); @@ -640,7 +640,7 @@ public class ActivityRecordTests extends WindowTestsBase { ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build(); finishingActivity.finishing = true; ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); - activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setState(STOPPED, "Testing"); assertEquals(false, activity.shouldMakeActive(null /* activeActivity */)); } @@ -649,15 +649,16 @@ public class ActivityRecordTests extends WindowTestsBase { public void testShouldResume_stackVisibility() { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setState(STOPPED, "Testing"); - doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null); + doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null); assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */)); - doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null); + doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) + .when(task).getVisibility(null); assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */)); - doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null); + doReturn(TASK_FRAGMENT_VISIBILITY_INVISIBLE).when(task).getVisibility(null); assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */)); } @@ -665,13 +666,13 @@ public class ActivityRecordTests extends WindowTestsBase { public void testShouldResumeOrPauseWithResults() { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setState(STOPPED, "Testing"); ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent()); topActivity.finishing = true; - doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null); + doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null); assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */)); assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */)); } @@ -684,7 +685,7 @@ public class ActivityRecordTests extends WindowTestsBase { .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT) .build(); final Task task = activity.getTask(); - activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setState(STOPPED, "Testing"); final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); try { @@ -727,7 +728,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build(); topActivity.setOccludesParent(false); - activity.setState(Task.ActivityState.STOPPED, "Testing"); + activity.setState(STOPPED, "Testing"); activity.setVisibility(true); activity.makeActiveIfNeeded(null /* activeActivity */); assertEquals(STARTED, activity.getState()); @@ -1011,8 +1012,8 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() { final ActivityRecord activity = createActivityWithTask(); - final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED}; - for (ActivityState state : states) { + final State[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED}; + for (State state : states) { activity.finishing = false; activity.setState(state, "test"); reset(activity); @@ -1147,7 +1148,7 @@ public class ActivityRecordTests extends WindowTestsBase { /** * Verify that finish request won't change the state of next top activity if the current * finishing activity doesn't need to be destroyed immediately. The case is usually like - * from {@link ActivityStack#completePauseLocked(boolean, ActivityRecord)} to + * from {@link Task#completePause(boolean, ActivityRecord)} to * {@link ActivityRecord#completeFinishing(String)}, so the complete-pause should take the * responsibility to resume the next activity with updating the state. */ @@ -1393,7 +1394,7 @@ public class ActivityRecordTests extends WindowTestsBase { } private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask, - ActivityState secondActivityState) { + State secondActivityState) { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -1445,7 +1446,8 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testDestroyIfPossible() { final ActivityRecord activity = createActivityWithTask(); - doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities(); + doReturn(false).when(mRootWindowContainer) + .resumeFocusedTasksTopActivities(); activity.destroyIfPossible("test"); assertEquals(DESTROYING, activity.getState()); @@ -1467,7 +1469,8 @@ public class ActivityRecordTests extends WindowTestsBase { homeStack.removeChild(t, "test"); }, true /* traverseTopToBottom */); activity.finishing = true; - doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities(); + doReturn(false).when(mRootWindowContainer) + .resumeFocusedTasksTopActivities(); // Try to destroy the last activity above the home stack. activity.destroyIfPossible("test"); @@ -1903,8 +1906,7 @@ public class ActivityRecordTests extends WindowTestsBase { assertTrue(wpc.registeredForActivityConfigChanges()); // Create a new task with custom config to reparent the activity to. - final Task newTask = - new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build(); + final Task newTask = new TaskBuilder(mSupervisor).build(); final Configuration newConfig = newTask.getConfiguration(); newConfig.densityDpi += 100; newTask.onRequestedOverrideConfigurationChanged(newConfig); @@ -1936,8 +1938,7 @@ public class ActivityRecordTests extends WindowTestsBase { .diff(wpc.getRequestedOverrideConfiguration())); // Create a new task with custom config to reparent the second activity to. - final Task newTask = - new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build(); + final Task newTask = new TaskBuilder(mSupervisor).build(); final Configuration newConfig = newTask.getConfiguration(); newConfig.densityDpi += 100; newTask.onRequestedOverrideConfigurationChanged(newConfig); @@ -2147,7 +2148,7 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity.supportsPictureInPicture()); } - private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state, + private void verifyProcessInfoUpdate(ActivityRecord activity, State state, boolean shouldUpdate, boolean activityChange) { reset(activity.app); activity.setState(state, "test"); @@ -2557,7 +2558,8 @@ public class ActivityRecordTests extends WindowTestsBase { any(), any(), anyBoolean(), anyBoolean(), any(), any()); // In normal case, resumeFocusedTasksTopActivities() should be called after // startActivityLocked(). So skip resumeFocusedTasksTopActivities() in ActivityBuilder. - doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities(); + doReturn(false).when(mRootWindowContainer) + .resumeFocusedTasksTopActivities(); // Make mVisibleSetFromTransferredStartingWindow true. final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build(); task.startActivityLocked(middle, null /* focusedTopActivity */, @@ -2700,6 +2702,40 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test + public void testCloseToSquareFixedOrientationPortrait() { + // create a square display + final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000) + .setSystemDecorations(true).build(); + final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build(); + + // create a fixed portrait activity + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task) + .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build(); + + // check that both the configuration and app bounds are portrait + assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation); + assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width() + <= activity.getConfiguration().windowConfiguration.getAppBounds().height()); + } + + @Test + public void testCloseToSquareFixedOrientationLandscape() { + // create a square display + final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000) + .setSystemDecorations(true).build(); + final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build(); + + // create a fixed landscape activity + final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task) + .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE).build(); + + // check that both the configuration and app bounds are landscape + assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation); + assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width() + > activity.getConfiguration().windowConfiguration.getAppBounds().height()); + } + + @Test public void testSetVisibility_visibleToVisible() { final ActivityRecord activity = new ActivityBuilder(mAtm) .setCreateTask(true).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 741f33f8aca7..8ca14bc0bb86 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -26,6 +26,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -244,7 +248,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()) .build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); - activity.setState(Task.ActivityState.RESUMED, "test"); + activity.setState(RESUMED, "test"); mSupervisor.endDeferResume(); assertEquals(activity.app, mAtm.mInternal.getTopApp()); @@ -254,13 +258,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { activity.mVisibleRequested = false; activity.setVisible(false); activity.getTask().setPausingActivity(activity); - homeActivity.setState(Task.ActivityState.PAUSED, "test"); + homeActivity.setState(PAUSED, "test"); // Even the visibility states are invisible, the next activity should be resumed because // the crashed activity was pausing. mAtm.mInternal.handleAppDied(activity.app, false /* restarting */, null /* finishInstrumentationCallback */); - assertEquals(Task.ActivityState.RESUMED, homeActivity.getState()); + assertEquals(RESUMED, homeActivity.getState()); assertEquals(homeActivity.app, mAtm.mInternal.getTopApp()); } @@ -271,7 +275,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { final Task rootHomeTask = mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask(); final ActivityRecord homeActivity = new ActivityBuilder(mAtm).setTask(rootHomeTask).build(); final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); - topActivity.setState(Task.ActivityState.RESUMED, "test"); + topActivity.setState(RESUMED, "test"); final Consumer<ActivityRecord> assertTopNonSleeping = activity -> { assertFalse(mAtm.mInternal.isSleeping()); @@ -287,7 +291,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { verify(mSupervisor.mGoingToSleepWakeLock).acquire(); doReturn(true).when(mSupervisor.mGoingToSleepWakeLock).isHeld(); - assertEquals(Task.ActivityState.PAUSING, topActivity.getState()); + assertEquals(PAUSING, topActivity.getState()); assertTrue(mAtm.mInternal.isSleeping()); assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING, mAtm.mInternal.getTopProcessState()); @@ -298,7 +302,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { final Task topRootTask = topActivity.getRootTask(); doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean()); doReturn(true).when(topRootTask).goToSleepIfPossible(anyBoolean()); - topActivity.setState(Task.ActivityState.STOPPING, "test"); + topActivity.setState(STOPPING, "test"); topActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */, null /* description */); verify(mSupervisor.mGoingToSleepWakeLock).release(); 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 31d46125fd70..af21e02ce27c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -35,10 +35,10 @@ import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; +import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; -import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID; import static com.google.common.truth.Truth.assertThat; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index d5628fc9de48..c4faaa31e5a0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -61,6 +61,7 @@ import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; +import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; import com.google.android.collect.Lists; @@ -566,6 +567,31 @@ public class DisplayAreaTest extends WindowTestsBase { .onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea); } + @Test + public void testGetDisplayAreaInfo() { + final DisplayArea<WindowContainer> displayArea = new DisplayArea<>( + mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST); + mDisplayContent.addChild(displayArea, 0); + final DisplayAreaInfo info = displayArea.getDisplayAreaInfo(); + + assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken()); + assertThat(info.configuration).isEqualTo(displayArea.getConfiguration()); + assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId()); + assertThat(info.featureId).isEqualTo(displayArea.mFeatureId); + assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId); + + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + final int tdaIndex = tda.getParent().mChildren.indexOf(tda); + final RootDisplayArea root = + new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1); + mDisplayContent.addChild(root, tdaIndex + 1); + displayArea.reparent(root, 0); + + final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo(); + + assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId); + } + private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { private TestDisplayArea(WindowManagerService wms, Rect bounds) { super(wms, ANY, "half display area"); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index d498d4663d78..fb71dcbbdaee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1528,7 +1528,7 @@ public class DisplayContentTests extends WindowTestsBase { unblockDisplayRotation(mDisplayContent); final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); app.setVisible(false); - app.setState(Task.ActivityState.RESUMED, "test"); + app.setState(ActivityRecord.State.RESUMED, "test"); mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); mDisplayContent.mOpeningApps.add(app); final int newOrientation = getRotatedOrientation(mDisplayContent); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 03304bb9456a..3741d499bc07 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -24,7 +24,7 @@ import static android.view.InsetsState.ITYPE_TOP_GESTURES; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; @@ -50,13 +50,13 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import static org.testng.Assert.expectThrows; import android.graphics.Insets; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.Pair; @@ -69,7 +69,6 @@ import android.view.PrivacyIndicatorBounds; import android.view.RoundedCorners; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; -import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -109,12 +108,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from // changing those frames. - doNothing().when(mWindow).computeFrame(); - - final WindowManager.LayoutParams attrs = mWindow.mAttrs; - attrs.width = MATCH_PARENT; - attrs.height = MATCH_PARENT; - attrs.format = PixelFormat.TRANSLUCENT; + doNothing().when(mWindow).computeFrame(any()); spyOn(mStatusBarWindow); spyOn(mNavBarWindow); @@ -219,7 +213,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test - public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() { + public void addingWindow_InWindowTypeWithPredefinedInsets() { mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar"); win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR}; @@ -230,7 +224,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { InsetsSourceProvider provider = mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR); - assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); + if (INSETS_LAYOUT_GENERALIZATION) { + // In the new flexible insets setup, the insets frame should always respect the window + // layout result. + assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); + } else { + assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); + } } @Test @@ -733,10 +733,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void testFixedRotationInsetsSourceFrame() { + mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT; + mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH; doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent) .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord)); - mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController() - .getRawInsetsState().peekSource(ITYPE_STATUS_BAR)); + mWindow.mAboveInsetsState.set( + mDisplayContent.getInsetsStateController().getRawInsetsState()); final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) .getSource(ITYPE_STATUS_BAR).getFrame(); mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index b793be74c033..b99808887aab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -37,7 +37,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT; import static org.junit.Assert.assertEquals; @@ -107,8 +106,7 @@ public class DisplayPolicyTests extends WindowTestsBase { @Test public void testChooseNavigationColorWindowLw() { - final WindowState opaque = createOpaqueFullscreen(false); - + final WindowState candidate = createOpaqueFullscreen(false); final WindowState dimmingImTarget = createDimmingDialogWindow(true); final WindowState dimmingNonImTarget = createDimmingDialogWindow(false); @@ -116,45 +114,51 @@ public class DisplayPolicyTests extends WindowTestsBase { final WindowState invisibleIme = createInputMethodWindow(false, true, false); final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false); - // If everything is null, return null + // If everything is null, return null. assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw( - null, null, null, NAV_BAR_BOTTOM)); + null, null, NAV_BAR_BOTTOM)); - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, null, NAV_BAR_BOTTOM)); + // If no IME windows, return candidate window. + assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( + candidate, null, NAV_BAR_BOTTOM)); assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, null, NAV_BAR_BOTTOM)); + dimmingImTarget, null, NAV_BAR_BOTTOM)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM)); + dimmingNonImTarget, null, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - null, null, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + // If IME is not visible, return candidate window. + assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw( + null, invisibleIme, NAV_BAR_BOTTOM)); + assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( + candidate, invisibleIme, NAV_BAR_BOTTOM)); + assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + dimmingImTarget, invisibleIme, NAV_BAR_BOTTOM)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + dimmingNonImTarget, invisibleIme, NAV_BAR_BOTTOM)); + + // If IME is visible, return candidate when the candidate window is not dimming. assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, visibleIme, NAV_BAR_BOTTOM)); + null, visibleIme, NAV_BAR_BOTTOM)); assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + candidate, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, visibleIme, NAV_BAR_RIGHT)); + // If IME is visible and the candidate window is dimming, checks whether the dimming window + // can be IME tartget or not. + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color // window. - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw( + null, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw( + candidate, imeNonDrawNavBar, NAV_BAR_BOTTOM)); assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); } @UseTestDisplay(addWindows = { W_NAVIGATION_BAR }) @@ -182,50 +186,21 @@ public class DisplayPolicyTests extends WindowTestsBase { // If there is no window, APPEARANCE_LIGHT_NAVIGATION_BARS is not allowed. assertEquals(0, - displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, - null, null)); - - // Opaque top fullscreen window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag. - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar)); - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, opaqueDarkNavBar, null, - opaqueDarkNavBar)); - assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, - displayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar, - opaqueLightNavBar, null, opaqueLightNavBar)); - assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, - displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, - opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar)); + displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null)); // Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS. + assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, dimming)); assertEquals(0, displayPolicy.updateLightNavigationBarLw( - 0, opaqueDarkNavBar, dimming, null, dimming)); - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - 0, opaqueLightNavBar, dimming, null, dimming)); - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, dimming, null, dimming)); - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, null, dimming)); - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, imeDrawLightNavBar, - dimming)); - - // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS - assertEquals(0, displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar, - imeDrawDarkNavBar)); + APPEARANCE_LIGHT_NAVIGATION_BARS, dimming)); - // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins. + // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag. + assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar)); assertEquals(0, displayPolicy.updateLightNavigationBarLw( - APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, opaqueLightNavBar, - imeDrawDarkNavBar, imeDrawDarkNavBar)); - - // IME window should be able to use APPEARANCE_LIGHT_NAVIGATION_BARS. - assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, - displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar, - opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar)); + APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar)); + assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw( + 0, opaqueLightNavBar)); + assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw( + APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar)); } @UseTestDisplay(addWindows = W_ACTIVITY) @@ -313,7 +288,9 @@ public class DisplayPolicyTests extends WindowTestsBase { displayInfo.logicalHeight = 2000; displayInfo.rotation = ROTATION_0; - displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); + WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs; + displayPolicy.addWindowLw(mNavBarWindow, attrs); + mNavBarWindow.setRequestedSize(attrs.width, attrs.height); mNavBarWindow.getControllableInsetProvider().setServerVisible(true); final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); mImeWindow.mAboveInsetsState.set(state); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 683ed889d283..3982a83d7778 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -58,8 +58,6 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int DISPLAY_HEIGHT = 1000; static final int DISPLAY_DENSITY = 320; - static final int STATUS_BAR_HEIGHT = 10; - static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; static final int IME_HEIGHT = 415; diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index bf3ed692dc8e..5b04c91d5a63 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -80,7 +81,7 @@ public class InsetsPolicyTest extends WindowTestsBase { } @Test - public void testControlsForDispatch_dockedStackVisible() { + public void testControlsForDispatch_dockedTaskVisible() { addWindow(TYPE_STATUS_BAR, "statusBar"); addWindow(TYPE_NAVIGATION_BAR, "navBar"); @@ -93,25 +94,26 @@ public class InsetsPolicyTest extends WindowTestsBase { } @Test - public void testControlsForDispatch_freeformStackVisible() { + public void testControlsForDispatch_multiWindowTaskVisible() { addWindow(TYPE_STATUS_BAR, "statusBar"); addWindow(TYPE_NAVIGATION_BAR, "navBar"); - final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM, + final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app"); final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win); - // The app must not control any bars. + // The app must not control any system bars. assertNull(controls); } @Test - public void testControlsForDispatch_dockedDividerControllerResizing() { + public void testControlsForDispatch_freeformTaskVisible() { addWindow(TYPE_STATUS_BAR, "statusBar"); addWindow(TYPE_NAVIGATION_BAR, "navBar"); - mDisplayContent.getDockedDividerController().setResizing(true); - final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM, + ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app"); + final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win); // The app must not control any system bars. assertNull(controls); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index d017c19cac86..1b19a28a9790 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -31,8 +31,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 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.ActivityRecord.State.PAUSED; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; -import static com.android.server.wm.Task.ActivityState.PAUSED; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.google.common.truth.Truth.assertThat; diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 0c6545c2438f..7ebf7755ee4a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -36,22 +36,23 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 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.ActivityRecord.State.DESTROYED; +import static com.android.server.wm.ActivityRecord.State.DESTROYING; +import static com.android.server.wm.ActivityRecord.State.FINISHING; +import static com.android.server.wm.ActivityRecord.State.INITIALIZING; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.Task.ActivityState.DESTROYED; -import static com.android.server.wm.Task.ActivityState.DESTROYING; -import static com.android.server.wm.Task.ActivityState.FINISHING; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.RESUMED; -import static com.android.server.wm.Task.ActivityState.STOPPED; -import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; -import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE; +import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; @@ -280,11 +281,11 @@ public class RootTaskTests extends WindowTestsBase { public void testResumedActivity() { final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); final Task task = r.getTask(); - assertNull(task.getResumedActivity()); + assertNull(task.getTopResumedActivity()); r.setState(RESUMED, "testResumedActivity"); - assertEquals(r, task.getResumedActivity()); + assertEquals(r, task.getTopResumedActivity()); r.setState(PAUSING, "testResumedActivity"); - assertNull(task.getResumedActivity()); + assertNull(task.getTopResumedActivity()); } @Test @@ -295,15 +296,15 @@ public class RootTaskTests extends WindowTestsBase { final Task task = r.getTask(); // Ensure moving task between two root tasks updates resumed activity r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); - assertEquals(r, rootTask.getResumedActivity()); + assertEquals(r, rootTask.getTopResumedActivity()); final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT, false /* animate */, true /* deferResume*/, "testResumedActivityFromTaskReparenting"); - assertNull(rootTask.getResumedActivity()); - assertEquals(r, destRootTask.getResumedActivity()); + assertNull(rootTask.getTopResumedActivity()); + assertEquals(r, destRootTask.getTopResumedActivity()); } @Test @@ -314,15 +315,15 @@ public class RootTaskTests extends WindowTestsBase { final Task task = r.getTask(); // Ensure moving task between two root tasks updates resumed activity r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); - assertEquals(r, rootTask.getResumedActivity()); + assertEquals(r, rootTask.getTopResumedActivity()); final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build(); task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT, false /* animate */, false /* deferResume*/, "testResumedActivityFromActivityReparenting"); - assertNull(rootTask.getResumedActivity()); - assertEquals(r, destRootTask.getResumedActivity()); + assertNull(rootTask.getTopResumedActivity()); + assertEquals(r, destRootTask.getTopResumedActivity()); } @Test @@ -618,9 +619,9 @@ public class RootTaskTests extends WindowTestsBase { doReturn(false).when(splitScreenSecondary2).isTranslucent(any()); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); // First split-screen secondary should be visible behind another translucent split-screen @@ -628,9 +629,9 @@ public class RootTaskTests extends WindowTestsBase { doReturn(true).when(splitScreenSecondary2).isTranslucent(any()); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, @@ -642,13 +643,13 @@ public class RootTaskTests extends WindowTestsBase { assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, assistantRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); // Split-screen root tasks should be visible behind a translucent fullscreen root task. @@ -657,13 +658,13 @@ public class RootTaskTests extends WindowTestsBase { assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, assistantRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenSecondary2.getVisibility(null /* starting */)); // Assistant root task shouldn't be visible behind translucent split-screen root task, @@ -678,25 +679,25 @@ public class RootTaskTests extends WindowTestsBase { assertTrue(assistantRootTask.shouldBeVisible(null /* starting */)); assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, assistantRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); } else { assertFalse(assistantRootTask.shouldBeVisible(null /* starting */)); assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, assistantRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); } } @@ -707,45 +708,51 @@ public class RootTaskTests extends WindowTestsBase { WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */); final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */); + // Creating as two-level tasks so home task can be reparented to split-secondary root task. final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */); + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */, + true /* twoLevelTask */); doReturn(false).when(homeRootTask).isTranslucent(any()); doReturn(false).when(splitPrimary).isTranslucent(any()); doReturn(false).when(splitSecondary).isTranslucent(any()); - // Re-parent home to split secondary. homeRootTask.reparent(splitSecondary, POSITION_TOP); // Current tasks should be visible. - assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + splitPrimary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + splitSecondary.getVisibility(null /* starting */)); // Home task should still be visible even though it is a child of another visible task. - assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + homeRootTask.getVisibility(null /* starting */)); // Add fullscreen translucent task that partially occludes split tasks final Task translucentRootTask = createStandardRootTaskForVisibilityTest( WINDOWING_MODE_FULLSCREEN, true /* translucent */); // Fullscreen translucent task should be visible - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, translucentRootTask.getVisibility(null /* starting */)); // Split tasks should be visible behind translucent - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitPrimary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitSecondary.getVisibility(null /* starting */)); // Home task should be visible behind translucent since its parent is visible behind // translucent. - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, homeRootTask.getVisibility(null /* starting */)); // Hide split-secondary splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */); // Home split secondary and home task should be invisible. - assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + splitSecondary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + homeRootTask.getVisibility(null /* starting */)); } @Test @@ -757,9 +764,9 @@ public class RootTaskTests extends WindowTestsBase { createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, bottomRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, translucentRootTask.getVisibility(null /* starting */)); } @@ -775,10 +782,12 @@ public class RootTaskTests extends WindowTestsBase { createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + bottomRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, translucentRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + opaqueRootTask.getVisibility(null /* starting */)); } @Test @@ -793,10 +802,11 @@ public class RootTaskTests extends WindowTestsBase { createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + bottomRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, opaqueRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, translucentRootTask.getVisibility(null /* starting */)); } @@ -809,9 +819,9 @@ public class RootTaskTests extends WindowTestsBase { createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, bottomTranslucentRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, translucentRootTask.getVisibility(null /* starting */)); } @@ -824,9 +834,10 @@ public class RootTaskTests extends WindowTestsBase { createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN, false /* translucent */); - assertEquals(TASK_VISIBILITY_INVISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, bottomTranslucentRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + opaqueRootTask.getVisibility(null /* starting */)); } @Test @@ -840,16 +851,17 @@ public class RootTaskTests extends WindowTestsBase { final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, bottomRootTask.getVisibility(null /* starting */)); - assertEquals(TASK_VISIBILITY_VISIBLE, + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, translucentRootTask.getVisibility(null /* starting */)); // Add an activity to the pinned root task so it isn't considered empty for visibility // check. final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) .setTask(pinnedRootTask) .build(); - assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + pinnedRootTask.getVisibility(null /* starting */)); } @Test @@ -1142,12 +1154,12 @@ public class RootTaskTests extends WindowTestsBase { } else if (twoLevelTask) { task = new TaskBuilder(mSupervisor) .setTaskDisplayArea(taskDisplayArea) - .setWindowingMode(windowingMode) .setActivityType(activityType) .setOnTop(onTop) .setCreateActivity(true) .setCreateParentTask(true) .build().getRootTask(); + task.setWindowingMode(windowingMode); } else { task = new TaskBuilder(mSupervisor) .setTaskDisplayArea(taskDisplayArea) @@ -1301,9 +1313,9 @@ public class RootTaskTests extends WindowTestsBase { final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING; - task.startPausingLocked(false /* uiSleeping */, topActivity, + task.startPausing(false /* uiSleeping */, topActivity, "test"); - verify(task).completePauseLocked(anyBoolean(), eq(topActivity)); + verify(task).completePause(anyBoolean(), eq(topActivity)); } @Test @@ -1544,7 +1556,7 @@ public class RootTaskTests extends WindowTestsBase { activities[i] = r; doReturn(null).when(mAtm).getProcessController( eq(r.processName), eq(r.info.applicationInfo.uid)); - r.setState(Task.ActivityState.INITIALIZING, "test"); + r.setState(INITIALIZING, "test"); // Ensure precondition that the activity is opaque. assertTrue(r.occludesParent()); mSupervisor.startSpecificActivity(r, false /* andResume */, diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 9cf29d4dcc50..8d4acbbac993 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -31,13 +31,14 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.ActivityRecord.State.FINISHING; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE; -import static com.android.server.wm.Task.ActivityState.FINISHING; -import static com.android.server.wm.Task.ActivityState.PAUSED; -import static com.android.server.wm.Task.ActivityState.PAUSING; -import static com.android.server.wm.Task.ActivityState.STOPPED; -import static com.android.server.wm.Task.ActivityState.STOPPING; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.google.common.truth.Truth.assertThat; @@ -365,7 +366,7 @@ public class RootWindowContainerTests extends WindowTestsBase { TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea(); doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask(); mRootWindowContainer.applySleepTokens(true); - verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); + verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping(); verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( null /* target */, null /* targetOptions */); } @@ -386,7 +387,7 @@ public class RootWindowContainerTests extends WindowTestsBase { // landscape and the portrait lockscreen is shown. activity.setLastReportedConfiguration( new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig)); - activity.setState(Task.ActivityState.STOPPED, "sleep"); + activity.setState(STOPPED, "sleep"); display.setIsSleeping(true); doReturn(false).when(display).shouldSleep(); @@ -557,8 +558,8 @@ public class RootWindowContainerTests extends WindowTestsBase { doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); // Use the task as target to resume. - mRootWindowContainer.resumeFocusedTasksTopActivities( - rootTask, activity, null /* targetOptions */); + mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity, + null /* targetOptions */); // Verify the target task should resume its activity. verify(rootTask, times(1)).resumeTopActivityUncheckedLocked( @@ -626,7 +627,7 @@ public class RootWindowContainerTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, false /* onTop */)); final ActivityRecord activity = new ActivityBuilder(mAtm) .setTask(rootTask).setOnTop(true).build(); - activity.setState(Task.ActivityState.RESUMED, "test"); + activity.setState(RESUMED, "test"); // Assume the task is at the topmost position assertTrue(rootTask.isTopRootTaskInDisplayArea()); @@ -646,7 +647,7 @@ public class RootWindowContainerTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, false /* onTop */)); final ActivityRecord activity = new ActivityBuilder(mAtm) .setTask(rootTask).setOnTop(true).build(); - activity.setState(Task.ActivityState.RESUMED, "test"); + activity.setState(RESUMED, "test"); taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/); // Assume the task is at the topmost position @@ -774,7 +775,7 @@ public class RootWindowContainerTests extends WindowTestsBase { } /** - * Tests that when starting {@link #ResolverActivity} for home, it should use the standard + * Tests that when starting {@link ResolverActivity} for home, it should use the standard * activity type (in a new root task) so the order of back stack won't be broken. */ @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 4872ec511ccc..b47e6c1e544f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -40,8 +40,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; -import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.google.common.truth.Truth.assertThat; @@ -129,7 +131,7 @@ public class SizeCompatTests extends WindowTestsBase { doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity); mActivity.mVisibleRequested = true; mActivity.setSavedState(null /* savedState */); - mActivity.setState(Task.ActivityState.RESUMED, "testRestart"); + mActivity.setState(RESUMED, "testRestart"); prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); @@ -137,7 +139,7 @@ public class SizeCompatTests extends WindowTestsBase { // The visible activity should recompute configuration according to the last parent bounds. mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken); - assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState()); + assertEquals(RESTARTING_PROCESS, mActivity.getState()); assertNotEquals(originalOverrideBounds, mActivity.getBounds()); } @@ -584,7 +586,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testHandleActivitySizeCompatModeChanged() { setUpDisplaySizeWithApp(1000, 2000); doReturn(true).when(mTask).isOrganized(); - mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged"); + mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged"); prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); @@ -604,7 +606,7 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mVisibleRequested = true; mActivity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed - mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged"); + mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged"); mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity); // Expect null token when switching to non-size-compat mode activity. @@ -619,7 +621,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testHandleActivitySizeCompatModeChangedOnDifferentTask() { setUpDisplaySizeWithApp(1000, 2000); doReturn(true).when(mTask).isOrganized(); - mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged"); + mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged"); prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); @@ -639,7 +641,7 @@ public class SizeCompatTests extends WindowTestsBase { .setCreateActivity(true).build(); final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity(); doReturn(true).when(secondTask).isOrganized(); - secondActivity.setState(Task.ActivityState.RESUMED, + secondActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged"); prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); @@ -1535,6 +1537,30 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testSandboxDisplayApis_unresizableAppNotSandboxed() { + // Set up a display in landscape with an unresizable app. + setUpDisplaySizeWithApp(2500, 1000); + mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */); + prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE); + assertFitted(); + + // Activity max bounds not be sandboxed since sandboxing is disabled. + assertMaxBoundsInheritDisplayAreaBounds(); + } + + @Test + public void testSandboxDisplayApis_unresizableAppSandboxed() { + // Set up a display in landscape with an unresizable app. + setUpDisplaySizeWithApp(2500, 1000); + mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */); + prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE); + assertFitted(); + + // Activity max bounds should be sandboxed since sandboxing is enabled. + assertActivityMaxBoundsSandboxed(); + } + + @Test public void testResizableApp_notSandboxed() { // Set up a display in landscape with a fully resizable app. setUpDisplaySizeWithApp(2500, 1000); diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index b89539cabbfe..e32b2aa68b69 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -178,6 +178,11 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override + public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) { + return this; + } + + @Override public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect) { return this; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 67b273a5a82d..c45c18d16c38 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -37,8 +37,8 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; -import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.google.common.truth.Truth.assertThat; @@ -84,8 +84,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.mAdjacentTask = rootTask; - rootTask.mAdjacentTask = adjacentRootTask; + adjacentRootTask.setAdjacentTaskFragment(rootTask); taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); Task actualRootTask = taskDisplayArea.getLaunchRootTask( @@ -111,8 +110,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final Task adjacentRootTask = createTask( mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; - adjacentRootTask.mAdjacentTask = rootTask; - rootTask.mAdjacentTask = adjacentRootTask; + adjacentRootTask.setAdjacentTaskFragment(rootTask); taskDisplayArea.setLaunchRootTask(rootTask, new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD}); @@ -133,8 +131,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.mAdjacentTask = rootTask; - rootTask.mAdjacentTask = adjacentRootTask; + adjacentRootTask.setAdjacentTaskFragment(rootTask); taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); final Task actualRootTask = taskDisplayArea.getLaunchRootTask( diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 0ebff1d253ef..629e45208234 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -1257,7 +1257,8 @@ public class TaskTests extends WindowTestsBase { LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; spyOn(persister); - final Task task = getTestTask(); + final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true) + .setCreateParentTask(true).build().getRootTask(); task.setHasBeenVisible(false); task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index a1f89ec75784..efe65381b7df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -57,12 +57,14 @@ import org.mockito.Mockito; public class WindowFrameTests extends WindowTestsBase { private DisplayContent mTestDisplayContent; + private DisplayFrames mTestDisplayFrames; @Before public void setUp() throws Exception { DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo); testDisplayInfo.displayCutout = null; mTestDisplayContent = createNewDisplay(testDisplayInfo); + mTestDisplayFrames = mTestDisplayContent.mDisplayFrames; } // Do not use this function directly in the tests below. Instead, use more explicit function @@ -99,7 +101,7 @@ public class WindowFrameTests extends WindowTestsBase { // Here the window has FILL_PARENT, FILL_PARENT // so we expect it to fill the entire available frame. w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 1000, 1000); assertRelFrame(w, 0, 0, 1000, 1000); @@ -108,14 +110,14 @@ public class WindowFrameTests extends WindowTestsBase { // and we use mRequestedWidth/mRequestedHeight w.mAttrs.width = 300; w.mAttrs.height = 300; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // Explicit width and height without requested width/height // gets us nothing. assertFrame(w, 0, 0, 0, 0); w.mRequestedWidth = 300; w.mRequestedHeight = 300; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // With requestedWidth/Height we can freely choose our size within the // parent bounds. assertFrame(w, 0, 0, 300, 300); @@ -128,14 +130,14 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = -1; w.mAttrs.width = 100; w.mAttrs.height = 100; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 100, 100); w.mAttrs.flags = 0; // But sizes too large will be clipped to the containing frame w.mRequestedWidth = 1200; w.mRequestedHeight = 1200; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 1000, 1000); // Before they are clipped though windows will be shifted @@ -143,7 +145,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.y = 300; w.mRequestedWidth = 1000; w.mRequestedHeight = 1000; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 1000, 1000); // If there is room to move around in the parent frame the window will be shifted according @@ -153,18 +155,18 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = 300; w.mRequestedHeight = 300; w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 700, 0, 1000, 300); assertRelFrame(w, 700, 0, 1000, 300); w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 700, 700, 1000, 1000); assertRelFrame(w, 700, 700, 1000, 1000); // Window specified x and y are interpreted as offsets in the opposite // direction of gravity w.mAttrs.x = 100; w.mAttrs.y = 100; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 600, 600, 900, 900); assertRelFrame(w, 600, 600, 900, 900); } @@ -191,7 +193,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertEquals(resolvedTaskBounds, w.getFrame()); @@ -204,7 +206,7 @@ public class WindowFrameTests extends WindowTestsBase { final int cfBottom = logicalHeight / 2; final Rect cf = new Rect(0, 0, cfRight, cfBottom); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertEquals(resolvedTaskBounds, w.getFrame()); assertEquals(0, w.getRelativeFrame().left); assertEquals(0, w.getRelativeFrame().top); @@ -233,7 +235,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); @@ -249,7 +251,7 @@ public class WindowFrameTests extends WindowTestsBase { task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); task.setBounds(null); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, cf); } @@ -285,7 +287,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect winRect = new Rect(200, 200, 300, 500); task.setBounds(winRect); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, winRect.left, imeFrame.top - winRect.height(), winRect.right, imeFrame.top); // Now check that it won't get moved beyond the top @@ -293,7 +295,7 @@ public class WindowFrameTests extends WindowTestsBase { task.setBounds(winRect); w.setBounds(winRect); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, winRect.left, 0, winRect.right, winRect.height()); // Now we have status bar. Check that it won't go into the status bar area. @@ -301,14 +303,14 @@ public class WindowFrameTests extends WindowTestsBase { statusBarFrame.bottom = 60; state.getSource(ITYPE_STATUS_BAR).setFrame(statusBarFrame); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, winRect.left, statusBarFrame.bottom, winRect.right, statusBarFrame.bottom + winRect.height()); // Check that it's moved back without ime insets state.removeSource(ITYPE_IME); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertEquals(winRect, w.getFrame()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index a1b3159825fb..8fa7101dd05a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -42,8 +42,7 @@ 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.dx.mockito.inline.extended.ExtendedMockito.when; -import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; -import static com.android.server.wm.Task.ActivityState.RESUMED; +import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.WindowContainer.SYNC_STATE_READY; @@ -346,7 +345,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testDisplayAreaTransaction() { removeGlobalMinSizeRestriction(); - final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea"); + final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); testTransaction(displayArea); } @@ -364,7 +363,7 @@ public class WindowOrganizerTests extends WindowTestsBase { .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); testSetWindowingMode(rootTask); - final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea"); + final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea(); displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); testSetWindowingMode(displayArea); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index f848ce5e5579..1240f9ba84a0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -23,6 +23,12 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityRecord.State.PAUSED; +import static com.android.server.wm.ActivityRecord.State.PAUSING; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityRecord.State.STARTED; +import static com.android.server.wm.ActivityRecord.State.STOPPED; +import static com.android.server.wm.ActivityRecord.State.STOPPING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -309,17 +315,17 @@ public class WindowProcessControllerTests extends WindowTestsBase { callbackResult[0] = 0; activity.mVisibleRequested = false; - activity.setState(Task.ActivityState.PAUSED, "test"); + activity.setState(PAUSED, "test"); mWpc.computeOomAdjFromActivities(callback); assertEquals(paused, callbackResult[0]); callbackResult[0] = 0; - activity.setState(Task.ActivityState.STOPPING, "test"); + activity.setState(STOPPING, "test"); mWpc.computeOomAdjFromActivities(callback); assertEquals(stopping, callbackResult[0]); callbackResult[0] = 0; - activity.setState(Task.ActivityState.STOPPED, "test"); + activity.setState(STOPPED, "test"); mWpc.computeOomAdjFromActivities(callback); assertEquals(other, callbackResult[0]); } @@ -330,25 +336,25 @@ public class WindowProcessControllerTests extends WindowTestsBase { spyOn(tracker); final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; - activity.setState(Task.ActivityState.STARTED, "test"); + activity.setState(STARTED, "test"); verify(tracker).onAnyActivityVisible(mWpc); assertTrue(mWpc.hasVisibleActivities()); - activity.setState(Task.ActivityState.RESUMED, "test"); + activity.setState(RESUMED, "test"); verify(tracker).onActivityResumedWhileVisible(mWpc); assertTrue(tracker.hasResumedActivity(mWpc.mUid)); activity.makeFinishingLocked(); - activity.setState(Task.ActivityState.PAUSING, "test"); + activity.setState(PAUSING, "test"); assertFalse(tracker.hasResumedActivity(mWpc.mUid)); assertTrue(mWpc.hasForegroundActivities()); activity.setVisibility(false); activity.mVisibleRequested = false; - activity.setState(Task.ActivityState.STOPPED, "test"); + activity.setState(STOPPED, "test"); verify(tracker).onAllActivitiesInvisible(mWpc); assertFalse(mWpc.hasVisibleActivities()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 92b670ed9699..e83db78409b8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -946,4 +946,19 @@ public class WindowStateTests extends WindowTestsBase { assertNotNull(state.peekSource(ITYPE_IME)); assertTrue(state.getSource(ITYPE_IME).isVisible()); } + + @Test + public void testRequestedVisibility() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mActivityRecord.setVisible(false); + app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */); + assertFalse(app.isVisibleRequested()); + + // It doesn't have a surface yet, but should still be visible requested. + app.setHasSurface(false); + app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */); + + assertFalse(app.isVisible()); + assertTrue(app.isVisibleRequested()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 588089996d6c..050fd80411fc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; @@ -29,11 +30,13 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.os.Process.SYSTEM_UID; import static android.view.View.VISIBLE; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -83,10 +86,12 @@ import android.service.voice.IVoiceInteractionSession; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; +import android.view.Gravity; import android.view.IDisplayWindowInsetsController; import android.view.IWindow; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.View; @@ -132,6 +137,9 @@ class WindowTestsBase extends SystemServiceTestsBase { DisplayInfo mDisplayInfo = new DisplayInfo(); DisplayContent mDefaultDisplay; + static final int STATUS_BAR_HEIGHT = 10; + static final int NAV_BAR_HEIGHT = 15; + /** * It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with * {@link UseTestDisplay}, it will be an additional display. @@ -268,6 +276,14 @@ class WindowTestsBase extends SystemServiceTestsBase { } if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) { mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow"); + if (INSETS_LAYOUT_GENERALIZATION) { + mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT; + mStatusBarWindow.mAttrs.gravity = Gravity.TOP; + mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode = + LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mStatusBarWindow.setRequestedSize(WindowManager.LayoutParams.MATCH_PARENT, + STATUS_BAR_HEIGHT); + } } if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) { mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE, @@ -275,6 +291,15 @@ class WindowTestsBase extends SystemServiceTestsBase { } if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) { mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow"); + if (INSETS_LAYOUT_GENERALIZATION) { + mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT; + mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM; + mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + mNavBarWindow.mAttrs.paramsForRotation[rot] = + getNavBarLayoutParamsForRotation(rot); + } + } } if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) { mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, @@ -302,6 +327,37 @@ class WindowTestsBase extends SystemServiceTestsBase { waitUntilHandlersIdle(); } + private WindowManager.LayoutParams getNavBarLayoutParamsForRotation(int rotation) { + int width = WindowManager.LayoutParams.MATCH_PARENT; + int height = WindowManager.LayoutParams.MATCH_PARENT; + int gravity = Gravity.BOTTOM; + if (INSETS_LAYOUT_GENERALIZATION) { + switch (rotation) { + case ROTATION_UNDEFINED: + case Surface.ROTATION_0: + case Surface.ROTATION_180: + height = NAV_BAR_HEIGHT; + break; + case Surface.ROTATION_90: + gravity = Gravity.RIGHT; + width = NAV_BAR_HEIGHT; + break; + case Surface.ROTATION_270: + gravity = Gravity.LEFT; + width = NAV_BAR_HEIGHT; + break; + } + } + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR); + lp.width = width; + lp.height = height; + if (INSETS_LAYOUT_GENERALIZATION) { + lp.gravity = gravity; + } + return lp; + } + void beforeCreateTestDisplay() { // Called before display is created. } diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index bcd6ed73e133..824f91e1e826 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -45,6 +45,7 @@ public final class FrameworksTestsFilter extends SelectTest { // Test specifications for FrameworksMockingCoreTests. "android.app.activity.ActivityThreadClientTest", "android.view.DisplayTest", + "android.window.ConfigurationHelperTest", // Test specifications for FrameworksCoreTests. "android.app.servertransaction.", // all tests under the package. "android.view.CutoutSpecificationTest", @@ -59,10 +60,8 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.RoundedCornersTest", "android.view.WindowMetricsTest", "android.view.PendingInsetsControllerTest", - "android.window.WindowContextTest", - "android.window.WindowMetricsHelperTest", + "android.window.", // all tests under the package. "android.app.activity.ActivityThreadTest", - "android.window.WindowContextControllerTest" }; public FrameworksTestsFilter(Bundle testArgs) { |