Merge "Add orientation listener to all biometric overlays." into sc-dev am: b172012858 am: 9f2f76aad7
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15237859
Change-Id: I22f40286852ad3346033adef3273d040f4357cdd
diff --git a/Android.bp b/Android.bp
index 157bdcb..fb522973 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,6 +112,7 @@
":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 cfc4ea9..b0a95c4 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31072,8 +31072,8 @@
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 dd7c6db..2eafa93 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1281,6 +1281,12 @@
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
}
@@ -2120,6 +2126,7 @@
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";
@@ -3256,6 +3263,7 @@
@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 b31d8f70..765a2ba 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.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.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 @@
}
}
+ @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 @@
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 @@
// 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 @@
}
}
}
+
+ 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 0000000..f28015a
--- /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 923b6f4..1e76bbf 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -118,6 +118,6 @@
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 4a7fcd2..a836625 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -476,6 +476,19 @@
}
/**
+ * 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 3915abe..f6bcfd6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -18,7 +18,6 @@
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.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.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.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;
@@ -5428,6 +5430,12 @@
// behave properly when activity is relaunching.
r.window.clearContentView();
} else {
+ final ViewRootImpl viewRoot = v.getViewRootImpl();
+ if (viewRoot != null) {
+ // Clear the callback to avoid the destroyed activity from receiving
+ // configuration changes that are no longer effective.
+ viewRoot.setActivityConfigCallback(null);
+ }
wm.removeViewImmediate(v);
}
}
@@ -5751,7 +5759,7 @@
}
@Override
- public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
+ public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {
ArrayList<ComponentCallbacks2> callbacks
= new ArrayList<ComponentCallbacks2>();
@@ -5760,7 +5768,7 @@
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 +5778,12 @@
}
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 +5838,25 @@
// 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 ActivityClientRecord r = mActivities.get(activityToken);
+ final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig,
+ newConfig, r != null ? r.mSizeConfigurations : null);
+ 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 +5907,6 @@
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 +6050,8 @@
// 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 +6087,6 @@
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 +6266,7 @@
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 +6299,7 @@
}
final ArrayList<ComponentCallbacks2> callbacks =
- collectComponentCallbacks(true /* includeActivities */);
+ collectComponentCallbacks(true /* includeUiContexts */);
final int N = callbacks.size();
for (int i = 0; i < N; i++) {
@@ -7546,12 +7518,6 @@
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 +7872,6 @@
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 d91933c..bc698f6 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -32,11 +32,9 @@
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 f79e078..8637e31 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 @@
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 @@
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 @@
}
final ArrayList<ComponentCallbacks2> callbacks =
- mActivityThread.collectComponentCallbacks(false /* includeActivities */);
+ mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
freeTextLayoutCachesIfNeeded(configDiff);
@@ -238,13 +223,6 @@
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 @@
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 f52fdc5..249a606 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2721,10 +2721,13 @@
// need to override their display in ResourcesManager.
baseContext.mForceDisplayOverrideInResources = false;
baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
- baseContext.mDisplay = display;
final Resources windowContextResources = createWindowContextResources(baseContext);
baseContext.setResources(windowContextResources);
+ // Associate the display with window context resources so that configuration update from
+ // the server side will also apply to the display's metrics.
+ baseContext.mDisplay = ResourcesManager.getInstance()
+ .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
return baseContext;
}
@@ -3187,12 +3190,6 @@
@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 74d51a0..2be7803 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -330,4 +330,18 @@
* 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);
+
+ /**
+ * Marks a process as a delegate for the currently playing remote transition animation. This
+ * must be called from a process that is already a remote transition player or delegate. Any
+ * marked delegates are cleaned-up automatically at the end of the transition.
+ * @param caller is the IApplicationThread representing the calling process.
+ */
+ void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 20afffc..af75c69 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -693,7 +693,7 @@
* @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 444cc4e..1569e60 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 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)}.
*/
@@ -232,6 +239,12 @@
*/
public boolean isVisible;
+ /**
+ * Whether this task is sleeping due to sleeping display.
+ * @hide
+ */
+ public boolean isSleeping;
+
TaskInfo() {
// Do nothing
}
@@ -318,11 +331,10 @@
}
/**
- * 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;
@@ -330,12 +342,14 @@
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;
}
/**
@@ -389,8 +403,10 @@
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ isSleeping = source.readBoolean();
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
+ displayAreaFeatureId = source.readInt();
}
/**
@@ -426,8 +442,10 @@
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeBoolean(isSleeping);
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
+ dest.writeInt(displayAreaFeatureId);
}
@Override
@@ -453,8 +471,10 @@
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " isSleeping=" + isSleeping
+ " 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 fad6cd3..ebc2945 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -24,6 +24,7 @@
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 @@
/** @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 2724166..a647983 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3056,9 +3056,6 @@
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;
@@ -3151,10 +3148,6 @@
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 3cd13a2..17d4ae6 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -18,16 +18,23 @@
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 @@
* 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 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 9198eb7..89612fe 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -170,8 +170,8 @@
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 @@
@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 881e0cf..42fa9fb 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -589,7 +589,7 @@
*/
@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 @@
mConfigTracker.onInitialize(configChanges);
mPrivOps.set(privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
- updateInputMethodDisplay(displayId);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -629,29 +628,13 @@
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 2ed0bad..308e6d5 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -189,13 +189,11 @@
}
@UnsupportedAppUsage
- @Deprecated
public File getExternalStorageDirectory() {
return getExternalDirs()[0];
}
@UnsupportedAppUsage
- @Deprecated
public File getExternalStoragePublicDirectory(String type) {
return buildExternalStoragePublicDirs(type)[0];
}
@@ -698,11 +696,7 @@
*
* @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 @@
* @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 431bf4c..e7e53b3 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -597,6 +597,14 @@
@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 8021636..f4539c2 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -814,9 +814,10 @@
* @param displayId The display associated with the window context
* @param options A bundle used to pass window-related options and choose the right DisplayArea
*
- * @return {@code true} if the WindowContext is attached to the DisplayArea successfully.
+ * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
+ * attached to the DisplayArea successfully. {@code null}, otherwise.
*/
- boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
+ Configuration attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
in Bundle options);
/**
@@ -865,4 +866,11 @@
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 5a34a92..1cb1439 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -20,8 +20,8 @@
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 69ff64f..40942ea7 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -4008,6 +4008,22 @@
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 @@
toolMajor = 0;
toolMinor = 0;
orientation = 0;
+ relativeX = 0;
+ relativeY = 0;
}
/**
@@ -4053,6 +4071,8 @@
toolMajor = other.toolMajor;
toolMinor = other.toolMinor;
orientation = other.orientation;
+ relativeX = other.relativeX;
+ relativeY = other.relativeY;
}
/**
@@ -4084,6 +4104,10 @@
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 @@
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/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
index a78036f..e1cc604 100644
--- a/core/java/android/view/RemoteAnimationAdapter.java
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -17,6 +17,7 @@
package android.view;
import android.app.ActivityOptions;
+import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -58,6 +59,9 @@
private int mCallingPid;
private int mCallingUid;
+ /** @see #getCallingApplication */
+ private IApplicationThread mCallingApplication;
+
/**
* @param runner The interface that gets notified when we actually need to start the animation.
* @param duration The duration of the animation.
@@ -81,11 +85,19 @@
this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
}
+ @UnsupportedAppUsage
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay, IApplicationThread callingApplication) {
+ this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
+ mCallingApplication = callingApplication;
+ }
+
public RemoteAnimationAdapter(Parcel in) {
mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
mDuration = in.readLong();
mStatusBarTransitionDelay = in.readLong();
mChangeNeedsSnapshot = in.readBoolean();
+ mCallingApplication = IApplicationThread.Stub.asInterface(in.readStrongBinder());
}
public IRemoteAnimationRunner getRunner() {
@@ -126,6 +138,15 @@
return mCallingUid;
}
+ /**
+ * Gets the ApplicationThread that will run the animation. Instead it is intended to pass the
+ * calling information among client processes (eg. shell + launcher) through one-way binder
+ * calls (where binder itself doesn't track calling information).
+ */
+ public IApplicationThread getCallingApplication() {
+ return mCallingApplication;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -137,6 +158,7 @@
dest.writeLong(mDuration);
dest.writeLong(mStatusBarTransitionDelay);
dest.writeBoolean(mChangeNeedsSnapshot);
+ dest.writeStrongInterface(mCallingApplication);
}
public static final @android.annotation.NonNull Creator<RemoteAnimationAdapter> CREATOR
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 4e2f37f..b300f30 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -162,6 +162,8 @@
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,
@@ -548,6 +550,15 @@
*/
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.
@@ -3167,6 +3178,17 @@
/**
* @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 4f2cf6d..f04530f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1559,12 +1559,21 @@
* @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 2806850..34c4e5d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -291,6 +291,21 @@
*/
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 55beae0f..fcfb0ab 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -97,6 +97,7 @@
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 @@
*/
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 @@
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 10;
+ int TRANSIT_FIRST_CUSTOM = 11;
/**
* @hide
@@ -418,6 +424,7 @@
TRANSIT_KEYGUARD_GOING_AWAY,
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_KEYGUARD_UNOCCLUDE,
+ TRANSIT_PIP,
TRANSIT_FIRST_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@@ -467,6 +474,13 @@
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 @@
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 @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 @@
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 @@
} 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 @@
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 @@
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 @@
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 dd81dd9..aac09b8 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 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 @@
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 @@
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 @@
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 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
+ }
if (windows != null) {
sAccessibilityCache.setWindowsOnAllDisplays(windows);
return windows;
@@ -472,6 +501,15 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
mFindAccessibilityNodeInfoResult = info;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -936,6 +997,8 @@
}
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -975,13 +1038,15 @@
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 @@
mPerformAccessibilityActionResult = succeeded;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -1222,24 +1289,45 @@
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, params, mCallingUid);
+ 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, 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 f9cdbd3..17fad7e 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -111,7 +111,13 @@
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 @@
@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 @@
}
/**
- * Gets accessibility tracing enabled state.
+ * Gets accessibility interaction connection tracing enabled state.
*
* @hide
*/
- public boolean isAccessibilityTracingEnabled() {
+ public boolean isA11yInteractionConnectionTraceEnabled() {
synchronized (mLock) {
- return mIsAccessibilityTracingEnabled;
+ 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 isA11yServiceTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0);
}
}
@@ -1233,8 +1276,6 @@
(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 @@
notifyHighTextContrastStateChanged();
}
- updateAccessibilityTracingState(accessibilityTracingEnabled);
+ updateAccessibilityTracingState(stateFlags);
}
/**
@@ -1715,11 +1756,11 @@
}
/**
- * 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 d2db0df..5b2068f 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -96,8 +96,6 @@
*
* @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 @@
* @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 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 0000000..9a07975
--- /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 358467f..1a7aab6 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 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 @@
configuration.readFromParcel(in);
displayId = in.readInt();
featureId = in.readInt();
+ rootDisplayAreaId = in.readInt();
}
@Override
@@ -64,6 +76,7 @@
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 8784399..e674655 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -34,6 +34,15 @@
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 02aa1a9..7864c24 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 0000000..5eb432e
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -0,0 +1,52 @@
+/**
+ * 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.Bundle;
+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);
+
+ /**
+ * Called when the {@link WindowContainerTransaction} created with
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+ *
+ * @param errorCallbackToken Token set through {@link
+ * WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param exceptionBundle Bundle containing the exception. Should be created with
+ * {@link TaskFragmentOrganizer#putExceptionInBundle}.
+ */
+ void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
+}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
new file mode 100644
index 0000000..0ca8a86
--- /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 1223d72..39cdf5a 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -20,6 +20,7 @@
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 @@
/** @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 0000000..3729c09
--- /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 0000000..234b30c
--- /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 0000000..fde5089
--- /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 0000000..e4d6a6c
--- /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 0000000..461a736
--- /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 0000000..f864c91
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -0,0 +1,152 @@
+/*
+ * 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.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;
+
+ @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 WindowContainerToken token,
+ @NonNull Configuration configuration, boolean isEmpty, boolean isVisible) {
+ if (fragmentToken == null) {
+ throw new IllegalArgumentException("Invalid TaskFragmentInfo.");
+ }
+ mFragmentToken = fragmentToken;
+ mToken = token;
+ mConfiguration.setTo(configuration);
+ mIsEmpty = isEmpty;
+ mIsVisible = isVisible;
+ }
+
+ public IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ 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)
+ && mToken.equals(that.mToken)
+ && mIsEmpty == that.mIsEmpty
+ && mIsVisible == that.mIsVisible
+ && getWindowingMode() == that.getWindowingMode();
+ }
+
+ private TaskFragmentInfo(Parcel in) {
+ mFragmentToken = in.readStrongBinder();
+ 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(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
+ + " 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 0000000..78c55a2
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -0,0 +1,166 @@
+/*
+ * 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.Bundle;
+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 {
+
+ /**
+ * Key to the exception in {@link Bundle} in {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+ */
+ private static final String KEY_ERROR_CALLBACK_EXCEPTION = "fragment_exception";
+
+ /**
+ * Creates a {@link Bundle} with an exception that can be passed to
+ * {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+ */
+ public static Bundle putExceptionInBundle(@NonNull Throwable exception) {
+ final Bundle exceptionBundle = new Bundle();
+ exceptionBundle.putSerializable(KEY_ERROR_CALLBACK_EXCEPTION, exception);
+ return exceptionBundle;
+ }
+
+ /**
+ * 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) {}
+
+ /**
+ * Called when the {@link WindowContainerTransaction} created with
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+ *
+ * @param errorCallbackToken token set in
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param exception exception from the server side.
+ */
+ public void onTaskFragmentError(
+ @NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
+
+ 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));
+ }
+
+ @Override
+ public void onTaskFragmentError(
+ @NonNull IBinder errorCallbackToken, @NonNull Bundle exceptionBundle) {
+ mExecutor.execute(() -> TaskFragmentOrganizer.this.onTaskFragmentError(
+ errorCallbackToken,
+ (Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
+ }
+ };
+
+ private ITaskFragmentOrganizerController getController() {
+ try {
+ return getWindowOrganizerController().getTaskFragmentOrganizerController();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index c7c91cd..8fa0110 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -222,6 +222,7 @@
}
}
+
/**
* Restarts the top activity in the given task by killing its process if it is visible.
* @hide
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 141f47b..6351b03 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -18,6 +18,7 @@
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 @@
*/
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 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 @@
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 @@
// 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 @@
/** 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 void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mActivityType);
dest.writeIntArray(mModes);
+ dest.writeInt(mOrder);
}
@NonNull
@@ -209,7 +228,17 @@
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 23b8ee4..6430394 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.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 @@
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 @@
mRootLeash = new SurfaceControl();
mRootLeash.readFromParcel(in);
mRootOffset.readFromParcel(in);
+ mOptions = in.readTypedObject(AnimationOptions.CREATOR);
}
@Override
@@ -126,6 +136,7 @@
dest.writeList(mChanges);
mRootLeash.writeToParcel(dest, flags);
mRootOffset.writeToParcel(dest, flags);
+ dest.writeTypedObject(mOptions, flags);
}
@NonNull
@@ -154,6 +165,10 @@
mRootOffset.set(offsetLeft, offsetTop);
}
+ public void setAnimationOptions(AnimationOptions options) {
+ mOptions = options;
+ }
+
public int getType() {
return mType;
}
@@ -182,6 +197,10 @@
return mRootOffset;
}
+ public AnimationOptions getAnimationOptions() {
+ return mOptions;
+ }
+
@NonNull
public List<Change> getChanges() {
return mChanges;
@@ -484,4 +503,146 @@
+ 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 c0af572..9c512ad 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -20,6 +20,7 @@
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;
@@ -48,11 +49,15 @@
// Flat list because re-order operations are order-dependent
private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
+ @Nullable
+ private IBinder mErrorCallbackToken;
+
public WindowContainerTransaction() {}
private WindowContainerTransaction(Parcel in) {
in.readMap(mChanges, null /* loader */);
in.readList(mHierarchyOps, null /* loader */);
+ mErrorCallbackToken = in.readStrongBinder();
}
private Change getOrCreateChange(IBinder token) {
@@ -325,7 +330,8 @@
/**
* 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 +384,119 @@
}
/**
+ * 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;
+ }
+
+ /**
+ * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
+ * trigger callback with this {@param errorCallbackToken}.
+ * @param errorCallbackToken client provided token that will be passed back as parameter in
+ * the callback if there is an error on the server side.
+ * @see ITaskFragmentOrganizer#onTaskFragmentError
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
+ if (mErrorCallbackToken != null) {
+ throw new IllegalStateException("Can't set multiple error token for one transaction.");
+ }
+ mErrorCallbackToken = errorCallbackToken;
+ 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
@@ -398,6 +517,13 @@
mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
: new HierarchyOp(other.mHierarchyOps.get(i)));
}
+ if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
+ != other.mErrorCallbackToken) {
+ throw new IllegalArgumentException("Can't merge two WCT with different error token");
+ }
+ mErrorCallbackToken = mErrorCallbackToken != null
+ ? mErrorCallbackToken
+ : other.mErrorCallbackToken;
}
/** @hide */
@@ -415,11 +541,17 @@
return mHierarchyOps;
}
+ /** @hide */
+ @Nullable
+ public IBinder getErrorCallbackToken() {
+ return mErrorCallbackToken;
+ }
+
@Override
@NonNull
public String toString() {
return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
- + " }";
+ + " errorCallbackToken=" + mErrorCallbackToken + " }";
}
@Override
@@ -427,6 +559,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mChanges);
dest.writeList(mHierarchyOps);
+ dest.writeStrongBinder(mErrorCallbackToken);
}
@Override
@@ -705,6 +838,11 @@
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 +851,98 @@
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;
- final private int[] mWindowingModes;
- final private int[] mActivityTypes;
+ @Nullable
+ private int[] mWindowingModes;
- private final Bundle mLaunchOptions;
+ @Nullable
+ private int[] mActivityTypes;
+
+ @Nullable
+ private Bundle mLaunchOptions;
+
+ @Nullable
+ private Intent mActivityIntent;
+
+ // 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 +953,8 @@
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
+ mActivityIntent = copy.mActivityIntent;
+ mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
}
protected HierarchyOp(Parcel in) {
@@ -802,6 +965,8 @@
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
+ mActivityIntent = in.readTypedObject(Intent.CREATOR);
+ mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
}
public int getType() {
@@ -844,6 +1009,16 @@
return mLaunchOptions;
}
+ @Nullable
+ public Intent getActivityIntent() {
+ return mActivityIntent;
+ }
+
+ @Nullable
+ public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
+ return mTaskFragmentCreationOptions;
+ }
+
@Override
public String toString() {
switch (mType) {
@@ -868,6 +1043,19 @@
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 +1072,8 @@
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
+ dest.writeTypedObject(mActivityIntent, flags);
+ dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
}
@Override
@@ -902,5 +1092,96 @@
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 901625b..5d40085 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -26,7 +26,6 @@
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.os.IBinder;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -57,8 +56,14 @@
*
* @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) {
@@ -67,7 +72,7 @@
mType = type;
mOptions = options;
mWindowManager = createWindowContextWindowManager(this);
- IBinder token = getWindowContextToken();
+ WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
mController = new WindowContextController(token);
Reference.reachabilityFence(this);
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index d84f571..5aa6233 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -46,7 +47,7 @@
@VisibleForTesting
public boolean mAttachedToDisplayArea;
@NonNull
- private final IBinder mToken;
+ private final WindowTokenClient mToken;
/**
* Window Context Controller constructor
@@ -54,14 +55,13 @@
* @param token The token used to attach to a window manager node. It is usually from
* {@link Context#getWindowContextToken()}.
*/
- public WindowContextController(@NonNull IBinder token) {
- mToken = token;
- mWms = WindowManagerGlobal.getWindowManagerService();
+ public WindowContextController(@NonNull WindowTokenClient token) {
+ this(token, WindowManagerGlobal.getWindowManagerService());
}
/** Used for test only. DO NOT USE it in production code. */
@VisibleForTesting
- public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) {
+ public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
mWms = mockWms;
}
@@ -81,8 +81,15 @@
+ "a DisplayArea once.");
}
try {
- mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId,
- options);
+ final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
+ displayId, options);
+ if (configuration != null) {
+ mAttachedToDisplayArea = true;
+ // Send the DisplayArea's configuration to WindowContext directly instead of
+ // waiting for dispatching from WMS.
+ mToken.onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index b8619fb..5171adf 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -36,7 +36,6 @@
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 @@
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 @@
}
/**
+ * 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 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 6abf557..284b4b9 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,14 +15,25 @@
*/
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.Debug;
import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;
@@ -33,11 +44,13 @@
* {@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,12 +59,16 @@
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,
- * Bundle, boolean)}.<p/>
+ * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
+ * Bundle)}.<p/>
*
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
@@ -61,25 +78,85 @@
throw new IllegalStateException("Context is already attached.");
}
mContextRef = new WeakReference<>(context);
+ mConfiguration.setTo(context.getResources().getConfiguration());
+ mShouldDumpConfigForIme = Build.IS_DEBUGGABLE
+ && context instanceof AbstractInputMethodService;
}
+ /**
+ * Called when {@link Configuration} updates from the server side receive.
+ *
+ * @param newConfig the updated {@link Configuration}
+ * @param newDisplayId the updated {@link android.view.Display} ID
+ */
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+ }
+
+ /**
+ * Called when {@link Configuration} updates from the server side receive.
+ *
+ * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
+ * whether to dispatch configuration update or not.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
+ boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
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) {
+
+ if (shouldReportConfigChange && context instanceof WindowContext) {
+ final WindowContext windowContext = (WindowContext) context;
ActivityThread.currentActivityThread().getHandler().post(
- () -> ((WindowContext) context).dispatchConfigurationChanged(newConfig));
+ () -> windowContext.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 (shouldReportConfigChange && diff != 0
+ && context instanceof WindowProviderService) {
+ final WindowProviderService windowProviderService = (WindowProviderService) context;
+ ActivityThread.currentActivityThread().getHandler().post(
+ () -> windowProviderService.onConfigurationChanged(newConfig));
+ }
+ freeTextLayoutCachesIfNeeded(diff);
+ if (mShouldDumpConfigForIme) {
+ if (!shouldReportConfigChange) {
+ Log.d(TAG, "Only apply configuration update to Resources because "
+ + "shouldReportConfigChange is false.\n" + Debug.getCallers(5));
+ } else if (diff == 0) {
+ 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 60a8d80..d3224b1 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.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 @@
/** @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 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 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
}
/**
+ * 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 @@
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 @@
}
/**
+ * 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/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 10f14b4..4f940db 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.service.notification.StatusBarNotification;
+import android.view.InsetsState;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
@@ -182,7 +183,7 @@
/**
* Notifies System UI side of system bar attribute change on the specified display.
*
- * @param displayId the ID of the display to notify
+ * @param displayId the ID of the display to notify.
* @param appearance the appearance of the focused window. The light top bar appearance is not
* controlled here, but primaryAppearance and secondaryAppearance.
* @param appearanceRegions a set of appearances which will be only applied in their own bounds.
@@ -191,11 +192,12 @@
* stacks.
* @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
* @param behavior the behavior of the focused window.
- * @param isFullscreen whether any of status or navigation bar is requested invisible.
+ * @param requestedState the collection of the requested visibilities of system insets.
+ * @param packageName the package name of the focused app.
*/
void onSystemBarAttributesChanged(int displayId, int appearance,
in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- int behavior, boolean isFullscreen);
+ int behavior, in InsetsState requestedVisibilities, String packageName);
/**
* Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 8fb2f9c..2fd1691 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.view.InsetsState;
import com.android.internal.view.AppearanceRegion;
@@ -39,14 +40,15 @@
public final IBinder mImeToken;
public final boolean mNavbarColorManagedByIme;
public final int mBehavior;
- public final boolean mAppFullscreen;
+ public final InsetsState mRequestedState;
+ public final String mPackageName;
public final int[] mTransientBarTypes;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
- boolean navbarColorManagedByIme, int behavior, boolean appFullscreen,
- @NonNull int[] transientBarTypes) {
+ boolean navbarColorManagedByIme, int behavior, InsetsState requestedState,
+ String packageName, @NonNull int[] transientBarTypes) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
@@ -58,7 +60,8 @@
mImeToken = imeToken;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
- mAppFullscreen = appFullscreen;
+ mRequestedState = requestedState;
+ mPackageName = packageName;
mTransientBarTypes = transientBarTypes;
}
@@ -80,7 +83,8 @@
dest.writeStrongBinder(mImeToken);
dest.writeBoolean(mNavbarColorManagedByIme);
dest.writeInt(mBehavior);
- dest.writeBoolean(mAppFullscreen);
+ dest.writeTypedObject(mRequestedState, 0);
+ dest.writeString(mPackageName);
dest.writeIntArray(mTransientBarTypes);
}
@@ -104,12 +108,13 @@
final IBinder imeToken = source.readStrongBinder();
final boolean navbarColorManagedByIme = source.readBoolean();
final int behavior = source.readInt();
- final boolean appFullscreen = source.readBoolean();
+ final InsetsState requestedState = source.readTypedObject(InsetsState.CREATOR);
+ final String packageName = source.readString();
final int[] transientBarTypes = source.createIntArray();
return new RegisterStatusBarResult(icons, disabledFlags1, appearance,
appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher,
disabledFlags2, imeToken, navbarColorManagedByIme, behavior,
- appFullscreen, transientBarTypes);
+ requestedState, packageName, transientBarTypes);
}
@Override
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8d82e33..5354afb 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,7 +35,7 @@
* {@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 125182c..958205f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -230,6 +230,7 @@
"libbinderthreadstateutils",
"libdmabufinfo",
"libgif",
+ "libgui_window_info_static",
"libseccomp_policy",
"libgrallocusage",
"libscrypt_static",
@@ -370,6 +371,7 @@
"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 ec99d6d..7eb7ac4 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 463d909..afc44ff 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 @@
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 @@
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 de5bd6e..635480fc 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 @@
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 6971301..21db198 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 @@
jfieldID toolMajor;
jfieldID toolMinor;
jfieldID orientation;
+ jfieldID relativeX;
+ jfieldID relativeY;
} gPointerCoordsClassInfo;
static struct {
@@ -212,6 +215,12 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 1695e1a..dd80c73 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 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,
@@ -1881,6 +1894,8 @@
(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 1fc4a01..41fecfd 100644
--- a/core/proto/android/server/accessibilitytrace.proto
+++ b/core/proto/android/server/accessibilitytrace.proto
@@ -46,17 +46,17 @@
/* 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 fa1e9d4..0121bff 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -278,7 +278,7 @@
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 @@
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 @@
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 @@
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 029ed5b..2141477 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3607,8 +3607,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 2403a60..eb4919b 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 c65ef9a..53ba140 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 @@
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 @@
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 @@
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 @@
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 d1776fb..3d7d807 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.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 @@
}
@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 8643a37..b71d814 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 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/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 020f4a0..a6e351d 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -22,12 +22,15 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.IWindowManager;
@@ -38,6 +41,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Tests for {@link WindowContextController}
@@ -53,15 +58,18 @@
@Presubmit
public class WindowContextControllerTest {
private WindowContextController mController;
+ @Mock
private IWindowManager mMockWms;
+ @Mock
+ private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
- mMockWms = mock(IWindowManager.class);
- mController = new WindowContextController(new Binder(), mMockWms);
-
- doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(),
- anyInt(), any());
+ MockitoAnnotations.initMocks(this);
+ mController = new WindowContextController(mMockToken, mMockWms);
+ doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
+ doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
+ anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -85,6 +93,8 @@
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
+ verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
+ eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 272f228..7d4412c 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -24,6 +24,7 @@
import android.os.Parcel;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.view.InsetsState;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -59,7 +60,8 @@
new Binder() /* imeToken */,
true /* navbarColorManagedByIme */,
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
- true /* appFullscreen */,
+ new InsetsState() /* requestedState */,
+ "test" /* packageName */,
new int[0] /* transientBarTypes */);
final RegisterStatusBarResult copy = clone(original);
@@ -79,7 +81,8 @@
assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
assertThat(copy.mBehavior).isEqualTo(original.mBehavior);
- assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
+ assertThat(copy.mRequestedState).isEqualTo(original.mRequestedState);
+ assertThat(copy.mPackageName).isEqualTo(original.mPackageName);
assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes);
}
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 0000000..996d7b4
--- /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/car/Android.bp b/data/etc/car/Android.bp
index 084e1db..d1e4322 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -130,6 +130,13 @@
}
prebuilt_etc {
+ name: "allowed_privapp_com.google.android.car.adaslocation",
+ sub_dir: "permissions",
+ src: "com.google.android.car.adaslocation.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "allowed_privapp_com.google.android.car.kitchensink",
sub_dir: "permissions",
src: "com.google.android.car.kitchensink.xml",
diff --git a/data/etc/car/com.google.android.car.adaslocation.xml b/data/etc/car/com.google.android.car.adaslocation.xml
new file mode 100644
index 0000000..cc1ef3c
--- /dev/null
+++ b/data/etc/car/com.google.android.car.adaslocation.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<permissions>
+ <privapp-permissions package="com.google.android.car.adaslocation">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b67988e..50e6b90 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -79,12 +79,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-2029985709": {
- "message": "setFocusedTask: taskId=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_FOCUS",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"-2024464438": {
"message": "app-onAnimationFinished(): mOuter=%s",
"level": "DEBUG",
@@ -103,6 +97,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 +211,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 +253,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 +325,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 +409,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 +421,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 +481,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 +565,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 +631,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 +715,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 +817,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 +937,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 +997,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 +1219,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 +1261,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 +1285,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 +1327,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 +1387,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 +1399,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 +1489,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 +1597,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 +1627,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 +1639,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 +1657,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 +1723,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 +1771,18 @@
"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"
+ },
+ "-55185509": {
+ "message": "setFocusedTask: taskId=%d touchedActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.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 0000000..4e2a77f
--- /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 34c66a4..bf074b0 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 @@
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 1861e48..2f3214d 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.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 @@
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -89,11 +92,12 @@
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 @@
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mTaskToken, mTmpRect);
- // TODO(b/151449487): Enable synchronization
- mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.queue(wct);
}
/**
@@ -236,14 +239,16 @@
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 @@
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 58ca1fb..8286d10 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.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 @@
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 @@
/** 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 09fcb86e5..f3f66dc 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.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 @@
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 @@
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 @@
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler);
+ mainHandler, syncQueue);
}
/**
@@ -239,7 +242,8 @@
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
mContext = context;
mLauncherApps = launcherApps;
mBarService = statusBarService == null
@@ -262,6 +266,7 @@
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
mDisplayController = displayController;
+ mSyncQueue = syncQueue;
}
public void initialize() {
@@ -561,6 +566,10 @@
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 9687ec6..a02fa9b 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 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 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 @@
// The triangle pointing to the expanded view
private View mPointerView;
- private int mPointerMargin;
@Nullable private int[] mExpandedViewContainerLocation;
private AlphaOptimizedButton mManageButton;
@@ -102,9 +101,6 @@
*/
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 @@
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 @@
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 @@
updatePointerView();
}
- mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
if (mManageButton != null) {
int visibility = mManageButton.getVisibility();
removeView(mManageButton);
@@ -632,12 +624,11 @@
}
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 @@
}
}
- 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 @@
: 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 c600f56..0a856a8 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 androidx.annotation.VisibleForTesting;
+import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import java.lang.annotation.Retention;
@@ -58,6 +59,8 @@
/** 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ /** 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 92e455c..2c6136b 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 @@
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 @@
bubble.cleanupViews();
}
updatePointerPosition();
+ updateExpandedView();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
}
@@ -1815,9 +1818,10 @@
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 @@
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 @@
mExpandedViewContainerMatrix.setScale(
1f, 1f,
expandingFromBubbleAt + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ mPositioner.getExpandedViewY(mExpandedBubble, index));
}
mExpandedViewAlphaAnimator.reverse();
@@ -2077,7 +2081,7 @@
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 @@
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 df2b440..efe07fb 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 @@
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 @@
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 @@
}
}
- // 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 @@
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 a7996f05..6f63369 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 @@
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 5b158d2..8adfac0 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 @@
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 @@
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
+ 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;
+ }
+
+ // 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 (mRootBounds.equals(rootBounds)) {
- return false;
+ if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+ mRootBounds.set(rootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ resetDividerPosition();
+ affectsLayout = true;
}
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
- resetDividerPosition();
-
- // 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 @@
}
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 @@
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 362b40f..9bed40d 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 @@
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 d9409ec..b1fa2ac 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 @@
@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 @@
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 @@
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 @@
// 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 @@
}
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 f367cd6..efff3e3 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 @@
*/
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 @@
}
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 @@
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 PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
+ @NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
@@ -291,6 +261,7 @@
@ShellMainThread ShellExecutor mainExecutor) {
mContext = context;
mSyncTransactionQueue = syncTransactionQueue;
+ mPipTransitionState = pipTransitionState;
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
@@ -326,14 +297,14 @@
}
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 @@
* @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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
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 @@
*/
@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 @@
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 @@
@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 @@
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 @@
if (!mWaitForFixedRotation) {
return;
}
- if (mState == State.TASK_APPEARED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
if (mInSwipePipToHomeTransition) {
onEndOfSwipePipToHomeTransition();
} else {
@@ -788,9 +779,11 @@
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 @@
// 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 @@
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 @@
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 @@
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 void scheduleFinishResizePip(Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
@@ -1116,7 +1109,7 @@
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 void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
if (mWaitForFixedRotation) {
@@ -1383,7 +1376,7 @@
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 4759550..b75cde0 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 @@
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_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.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 @@
*/
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 @@
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 @@
throw new RuntimeException("Unrecognized animation type: "
+ mOneShotAnimationType);
}
+ startTransaction.apply();
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
@@ -158,6 +268,5 @@
}
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 d801c91..dedc566 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.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 @@
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 @@
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 @@
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 0000000..d23aada
--- /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 4f3ec96..62b50c5 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.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 @@
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 b7caf72..551476d 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 @@
@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/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 8f0892f..df1f5e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -20,6 +20,8 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.window.IRemoteTransition;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
@@ -77,9 +79,22 @@
int position, in Bundle options) = 9;
/**
- * Starts tasks simultaneously in one transition. The first task in the list will be in the
- * main-stage and on the left/top.
+ * Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
in Bundle sideOptions, int sidePosition, in IRemoteTransition remoteTransition) = 10;
+
+ /**
+ * Version of startTasks using legacy transition system.
+ */
+ oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
+ int sideTaskId, in Bundle sideOptions, int sidePosition,
+ in RemoteAnimationAdapter adapter) = 11;
+
+ /**
+ * Blocking call that notifies and gets additional split-screen targets when entering
+ * recents (for example: the dividerBar).
+ * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
+ */
+ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) = 12;
}
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 0000000..5d5a6e5
--- /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 0000000..71d48ee
--- /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 0000000..ea66180
--- /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 82f95a4..a0bdcc3 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 @@
/**
* 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 @@
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 002bfb6..d6afeba 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 @@
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 9a457b5..36b2777 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
@@ -39,6 +39,8 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.window.IRemoteTransition;
import androidx.annotation.BinderThread;
@@ -157,6 +159,10 @@
mStageCoordinator.exitSplitScreen();
}
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mStageCoordinator.onKeyguardOccludedChanged(occluded);
+ }
+
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
}
@@ -262,6 +268,11 @@
return options;
}
+ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+ if (!isSplitScreenVisible()) return null;
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
pw.println(prefix + TAG);
if (mStageCoordinator != null) {
@@ -284,6 +295,13 @@
mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
return mISplitScreen;
}
+
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onKeyguardOccludedChanged(occluded);
+ });
+ }
}
/**
@@ -417,6 +435,16 @@
}
@Override
+ public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+ int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ RemoteAnimationAdapter adapter) {
+ executeRemoteCallWithTaskPermission(mController, "startTasks",
+ (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
+ mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
+ adapter));
+ }
+
+ @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition,
@@ -444,5 +472,14 @@
controller.startIntent(intent, fillInIntent, stage, position, options);
});
}
+
+ @Override
+ public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+ final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
+ executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
+ (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel),
+ true /* blocking */);
+ return out[0];
+ }
}
}
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 c37789e..69d0be6 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 @@
}
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 0264c5a..4e91193 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
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -42,11 +43,21 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -115,7 +126,8 @@
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 +162,7 @@
mSyncQueue,
mSurfaceSession);
mSideStage = new SideStage(
+ mContext,
mTaskOrganizer,
mDisplayId,
mSideStageListener,
@@ -157,6 +170,10 @@
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);
@@ -241,6 +258,75 @@
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
}
+ /** Starts 2 tasks in one legacy transition. */
+ void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+ int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ RemoteAnimationAdapter adapter) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // Need to add another wrapper here in shell so that we can inject the divider bar
+ // and also manage the process elevation via setRunningRemote
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ RemoteAnimationTarget[] augmentedNonApps =
+ new RemoteAnimationTarget[nonApps.length + 1];
+ for (int i = 0; i < nonApps.length; ++i) {
+ augmentedNonApps[i] = nonApps[i];
+ }
+ augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ adapter.getCallingApplication());
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
+ finishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ try {
+ adapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+ };
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+ wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
+
+ if (mainOptions == null) {
+ mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+ } else {
+ ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+ mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+ }
+
+ sideOptions = sideOptions != null ? sideOptions : new Bundle();
+ setSideStagePosition(sidePosition);
+
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(getMainStageBounds(), wct);
+ mSideStage.setBounds(getSideStageBounds(), wct);
+
+ // Make sure the launch options will put tasks in the corresponding split roots
+ addActivityOptions(mainOptions, mMainStage);
+ addActivityOptions(sideOptions, mSideStage);
+
+ // Add task launch requests
+ wct.startTask(mainTaskId, mainOptions);
+ wct.startTask(sideTaskId, sideOptions);
+
+ // Using legacy transitions, so we can't use blast sync since it conflicts.
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
@SplitLayout.SplitPosition
int getSideStagePosition() {
return mSideStagePosition;
@@ -253,17 +339,17 @@
}
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 +361,12 @@
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,13 +495,27 @@
// 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) {
+ // When both stage's visibility changed to visible, main stage might receives visibility
+ // changed before side stage if it has higher z-order than side stage. Make sure we only
+ // update main stage's windowing mode with the visibility changed of side stage to prevent
+ // stacking multiple windowing mode transactions which result to flicker issue.
+ if (mainStageVisible && stageListener == mSideStageListener) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideStageVisible) {
// The main stage configuration should to follow split layout when side stage is
@@ -487,10 +593,6 @@
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.setBounds(getSideStageBounds(), wct);
- // Reorder side stage to the top whenever there's a new child task appeared in side
- // stage. This is needed to prevent main stage occludes side stage and makes main stage
- // flipping between fullscreen and multi-window windowing mode.
- wct.reorder(mSideStage.mRootTaskInfo.token, true);
mTaskOrganizer.applyTransaction(wct);
}
}
@@ -580,11 +682,18 @@
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 +781,8 @@
@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 +827,14 @@
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 +864,7 @@
// 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 */);
@@ -860,6 +970,16 @@
}
}
+ RemoteAnimationTarget getDividerBarLegacyTarget() {
+ final Rect bounds = mSplitLayout.getDividerBounds();
+ return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
+ mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */,
+ null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
+ new android.graphics.Point(0, 0) /* position */, bounds, bounds,
+ new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
+ null /* taskInfo */, TYPE_DOCK_DIVIDER);
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
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 c6fb5af..4eadf8c 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_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 @@
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 @@
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 @@
// 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 @@
}
@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 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.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
+ a = mTransitionAnimation.loadVoiceActivityExitAnimation(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 ((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.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToFrontEnterAnimation);
+ 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_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 (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.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_close_exit);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : R.styleable.WindowAnimation_activityCloseExitAnimation);
+ }
}
- } else if (changeMode == TRANSIT_TO_BACK && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToBackExitAnimation);
- }
+ } 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 @@
}
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 @@
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 @@
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 4da6664..6bd8053 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 @@
@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 @@
};
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 @@
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 9bfb261..f432049 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 @@
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 @@
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 @@
}
}
if (removed) {
- remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+ RemoteDeathHandler handler = mDeathHandlers.remove(remote.asBinder());
+ remote.asBinder().unlinkToDeath(handler, 0 /* flags */);
}
}
@@ -99,7 +95,8 @@
@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 @@
};
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 @@
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 @@
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 @@
+ " 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 60707cc..1ca71af 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 @@
/** 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;
@@ -91,12 +97,13 @@
private float mTransitionAnimationScaleSetting = 1.0f;
private static final class ActiveTransition {
- IBinder mToken = null;
- TransitionHandler mHandler = null;
- boolean mMerged = false;
- TransitionInfo mInfo = null;
- SurfaceControl.Transaction mStartT = null;
- SurfaceControl.Transaction mFinishT = null;
+ IBinder mToken;
+ TransitionHandler mHandler;
+ boolean mMerged;
+ boolean mAborted;
+ TransitionInfo mInfo;
+ SurfaceControl.Transaction mStartT;
+ SurfaceControl.Transaction mFinishT;
}
/** Keeps track of currently playing transitions in the order of receipt. */
@@ -382,7 +389,7 @@
}
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));
}
@@ -416,17 +423,19 @@
/** Special version of finish just for dealing with no-op/invalid transitions. */
private void onAbort(IBinder transition) {
- final int activeIdx = findActiveTransition(transition);
- if (activeIdx < 0) return;
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation aborted due to no-op, notifying core %s", transition);
- mActiveTransitions.remove(activeIdx);
- mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
+ onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */);
}
private void onFinish(IBinder transition,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
+ onFinish(transition, wct, wctCB, false /* abort */);
+ }
+
+ private void onFinish(IBinder transition,
+ @Nullable WindowContainerTransaction wct,
+ @Nullable WindowContainerTransactionCallback wctCB,
+ boolean abort) {
int activeIdx = findActiveTransition(transition);
if (activeIdx < 0) {
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
@@ -434,28 +443,37 @@
return;
} else if (activeIdx > 0) {
// This transition was merged.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
- transition);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged (abort=%b:"
+ + " %s", abort, transition);
final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mMerged = true;
+ active.mAborted = abort;
if (active.mHandler != null) {
active.mHandler.onTransitionMerged(active.mToken);
}
return;
}
+ mActiveTransitions.get(activeIdx).mAborted = abort;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation finished, notifying core %s", transition);
+ "Transition animation finished (abort=%b), notifying core %s", abort, transition);
// Merge all relevant transactions together
SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
final ActiveTransition toMerge = mActiveTransitions.get(iA);
if (!toMerge.mMerged) break;
+ // aborted transitions have no start/finish transactions
+ if (mActiveTransitions.get(iA).mStartT == null) break;
+ if (fullFinish == null) {
+ fullFinish = new SurfaceControl.Transaction();
+ }
// Include start. It will be a no-op if it was already applied. Otherwise, we need it
// to maintain consistent state.
fullFinish.merge(mActiveTransitions.get(iA).mStartT);
fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
}
- fullFinish.apply();
+ if (fullFinish != null) {
+ fullFinish.apply();
+ }
// Now perform all the finishes.
mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
@@ -464,6 +482,12 @@
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
}
+ // sift through aborted transitions
+ while (mActiveTransitions.size() > activeIdx
+ && mActiveTransitions.get(activeIdx).mAborted) {
+ ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
+ }
if (mActiveTransitions.size() <= activeIdx) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
+ "finished");
@@ -494,6 +518,12 @@
int mergeIdx = activeIdx + 1;
while (mergeIdx < mActiveTransitions.size()) {
ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
+ if (mergeCandidate.mAborted) {
+ // transition was aborted, so we can skip for now (still leave it in the list
+ // so that it gets cleaned-up in the right order).
+ ++mergeIdx;
+ continue;
+ }
if (mergeCandidate.mMerged) {
throw new IllegalStateException("Can't merge a transition after not-merging"
+ " a preceding one.");
@@ -566,12 +596,19 @@
* 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 0000000..2c668ed
--- /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 20ac5bf..1cbad15 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 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 @@
ShellTaskOrganizer mOrganizer;
@Mock
HandlerExecutor mExecutor;
+ @Mock
+ SyncTransactionQueue mSyncQueue;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -99,7 +103,14 @@
}).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 @@
@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 952dc31..e138595 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 @@
mSplitLayout = new SplitLayout(
"TestSplitLayout",
mContext,
- getConfiguration(false),
+ getConfiguration(),
mSplitLayoutHandler,
b -> b.setParent(mRootLeash),
mDisplayImeController,
@@ -71,9 +71,22 @@
@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 @@
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 9d7c82b..0270093 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 @@
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
+ private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private ComponentName mComponent1;
@@ -90,11 +91,12 @@
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 56a0056..69ead3a 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.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 @@
/** 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 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 aca80f3..b6da868 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 90b5b37..1a30f16 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -21,11 +21,13 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
+import android.os.SystemProperties;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -52,6 +54,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests {
+ private static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
+
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@@ -93,6 +98,8 @@
@Test
public void testChildTaskAppeared() {
+ // With shell transitions, the transition manages status changes, so skip this test.
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
@@ -110,6 +117,8 @@
@Test
public void testTaskVanished() {
+ // With shell transitions, the transition manages status changes, so skip this test.
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
mStageTaskListener.mRootTaskInfo = mRootTask;
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 2d2ab2c..a2b1f64 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 @@
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 @@
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -285,7 +287,7 @@
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 @@
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -358,9 +360,11 @@
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 @@
@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 bc9c203..0000000
--- 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 5588fd3..2430eec 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 f5d47ce..d722866 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -603,8 +603,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 18388a9..b5156b6 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 c4e9ff8..5796c19 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2986,9 +2986,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 277b2e3..de9558e 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 @@
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 7dffc26..e33985d 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,13 +16,24 @@
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.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.Message;
+import android.util.DisplayMetrics;
+import android.view.Surface;
/* Common code */
public class Utilities {
+ private static final float TABLET_MIN_DPS = 600;
+
/**
* Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
*/
@@ -31,6 +42,23 @@
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 +86,52 @@
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;
+ }
+
+ /** See {@link #isTablet(Configuration, Context)} */
+ public static boolean isTablet(Context context) {
+ Configuration newConfig = context.getResources().getConfiguration();
+ return isTablet(newConfig, context);
+ }
+
+ /**
+ * @return whether or not {@param newConfig} represents that of a large screen device or not
+ */
+ public static boolean isTablet(Configuration newConfig, Context context) {
+ float density = Resources.getSystem().getDisplayMetrics().density;
+ int size = Math.min((int) (density * newConfig.screenWidthDp),
+ (int) (density* newConfig.screenHeightDp));
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ return (size / densityRatio) >= TABLET_MIN_DPS;
+ }
}
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 0000000..5581a1c
--- /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 c468e41..4663a9a 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 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 @@
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 @@
? "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 ee55bf0..025d7ef 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
@@ -69,7 +69,8 @@
return mRemoteTransition;
}
- private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
+ /** Wraps a RemoteAnimationRunnerCompat in an IRemoteAnimationRunner. */
+ public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
@@ -260,7 +261,7 @@
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 653d730..a77cc87 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 @@
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 @@
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 @@
// 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 @@
}
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 @@
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 @@
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 @@
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 @@
mFinishCB = finishCB;
mPausingTask = pausingTask;
mLeashMap = leashMap;
+ mTransition = transition;
}
@SuppressLint("NewApi")
@@ -263,10 +273,13 @@
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 @@
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 @@
mInfo = null;
mOpeningLeash = null;
mLeashMap = null;
+ mTransition = null;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
@@ -318,6 +332,23 @@
@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 a5b2509..8f14cd8 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.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 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 @@
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 @@
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 @@
}
/**
- * 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 632919a..9e456cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -19,6 +19,8 @@
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.text.TextUtils;
import android.view.View;
@@ -245,10 +247,12 @@
}
/**
- * 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 ca4d73b..840e8c8 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.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 @@
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 @@
}
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 @@
lp.gravity = Gravity.CENTER_HORIZONTAL;
}
- securityView.setLayoutParams(lp);
+ mSecurityViewFlipper.setLayoutParams(lp);
}
/**
@@ -324,14 +303,12 @@
* 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 @@
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 @@
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 @@
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 fde8213..cb5c6c3 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.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.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 @@
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 @@
// 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 @@
KeyguardStateController keyguardStateController,
SecurityCallback securityCallback,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -216,6 +233,7 @@
mKeyguardSecurityCallback);
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
+ mFalsingCollector = falsingCollector;
}
@Override
@@ -440,13 +458,49 @@
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 @@
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 @@
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -545,7 +602,8 @@
UiEventLogger uiEventLogger,
KeyguardStateController keyguardStateController,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -556,6 +614,7 @@
mKeyguardStateController = keyguardStateController;
mSecurityViewFlipperController = securityViewFlipperController;
mConfigurationController = configurationController;
+ mFalsingCollector = falsingCollector;
}
public KeyguardSecurityContainerController create(
@@ -564,7 +623,7 @@
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 2096c31..2158401 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -19,6 +19,7 @@
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 @@
}
/**
- * 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 bd000b2..1344be6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2785,6 +2785,20 @@
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 @@
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 9849a7e..6aa7aaa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -88,6 +88,12 @@
*/
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 ca2c034..fa56453 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 @@
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 717c715..a51e3fc 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.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.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 @@
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 @@
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 @@
* @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);
- }
- } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
- onRotate();
- } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
- updateAccessibilityWindowTitleIfNeeded();
+ 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");
+ }
+ 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 @@
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 @@
}
}
- 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 @@
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 @@
: centerY - mMagnificationFrame.exactCenterY();
mScale = Float.isNaN(scale) ? mScale : scale;
- setMagnificationFrameBoundary();
+ calculateMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
if (!isWindowVisible()) {
createMirrorWindow();
@@ -764,6 +822,8 @@
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 c97a30e..83fa994 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.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 @@
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ TaskbarDelegate taskbarDelegate,
UserTracker userTracker) {
return new NavigationBarController(context,
windowManager,
@@ -261,6 +265,8 @@
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 bc4ced4..98fb3c9 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 @@
iWindowManager,
backgroundExecutor,
uiEventLogger,
- null,
ringerModeTracker,
sysUiState,
handler,
@@ -338,8 +337,7 @@
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 5acb303..2afce71 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 @@
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 @@
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 @@
mTelecomManager = telecomManager;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -653,7 +650,7 @@
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider, mStatusBar);
+ mStatusBar);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -2124,7 +2121,6 @@
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
- private GlobalActionsInfoProvider mInfoProvider;
private GestureDetector mGestureDetector;
private StatusBar mStatusBar;
@@ -2178,7 +2174,7 @@
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 @@
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
mStatusBar = statusBar;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
@@ -2309,10 +2304,6 @@
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 25837e3..0000000
--- 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 26f38dd..90afec6a 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.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;
@@ -40,10 +42,12 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
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_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 +58,7 @@
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 +70,7 @@
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;
@@ -89,6 +92,7 @@
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -101,7 +105,6 @@
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 +133,7 @@
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 +155,6 @@
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 +165,7 @@
* 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 +182,6 @@
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 +202,13 @@
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 +486,11 @@
@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 +511,11 @@
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mUserTracker = userTracker;
mNotificationShadeDepthController = notificationShadeDepthController;
mNavBarMode = mNavigationModeController.addListener(this);
- mAccessibilityButtonModeObserver.addListener(this);
}
public NavigationBarView getView() {
@@ -520,34 +523,17 @@
}
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 +591,8 @@
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 +614,7 @@
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -704,6 +689,7 @@
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
+ mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
}
@@ -722,6 +708,7 @@
* 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 +722,8 @@
refreshLayout(ld);
}
- repositionNavigationBar();
+ repositionNavigationBar(rotation);
if (canShowSecondaryHandle()) {
- int rotation = newConfig.windowConfiguration.getRotation();
if (rotation != mCurrentRotation) {
mCurrentRotation = rotation;
orientSecondaryHomeHandle();
@@ -889,30 +875,15 @@
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;
- mNavigationBarView.setNavigationIconHints(hints);
+ if (!isTablet(mContext)) {
+ // All IME functions handled by launcher via Sysui flags for large screen
+ mNavigationBarView.setNavigationIconHints(hints);
+ }
checkBarModes();
updateSystemUiStateFlags(-1);
}
@@ -984,7 +955,7 @@
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
if (displayId != mDisplayId) {
return;
}
@@ -1120,13 +1091,12 @@
|| (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 +1138,7 @@
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 +1359,8 @@
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 +1379,7 @@
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 +1389,8 @@
.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 +1406,6 @@
}
}
- /**
- * 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 +1507,6 @@
}
}
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- updateAccessibilityServicesState(mAccessibilityManager);
- }
-
public void disableAnimationsDuringHide(long delay) {
mNavigationBarView.setLayoutTransitionsEnabled(false);
mHandler.postDelayed(mEnableLayoutTransitions,
@@ -1604,22 +1531,110 @@
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 +1643,7 @@
}
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 0000000..13e6d8b
--- /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 b9e9240..6344c59 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,16 +19,15 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
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;
import android.view.Display;
@@ -86,8 +85,6 @@
ConfigurationController.ConfigurationListener,
NavigationModeController.ModeChangedListener, Dumpable {
- private static final float TABLET_MIN_DPS = 600;
-
private static final String TAG = NavigationBarController.class.getSimpleName();
private final Context mContext;
@@ -113,6 +110,7 @@
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 +155,8 @@
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ TaskbarDelegate taskbarDelegate,
UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
@@ -182,6 +182,7 @@
mSystemActions = systemActions;
mUiEventLogger = uiEventLogger;
mHandler = mainHandler;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -189,15 +190,17 @@
mNavBarOverlayController = navBarOverlayController;
mNavMode = mNavigationModeController.addListener(this);
mNavigationModeController.addListener(this);
- mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
- mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mTaskbarDelegate = taskbarDelegate;
+ mTaskbarDelegate.setOverviewProxyService(overviewProxyService,
+ navigationBarA11yHelper, mSysUiFlagsContainer);
+ mIsTablet = isTablet(mContext);
mUserTracker = userTracker;
}
@Override
public void onConfigChanged(Configuration newConfig) {
boolean isOldConfigTablet = mIsTablet;
- mIsTablet = isTablet(newConfig);
+ mIsTablet = isTablet(newConfig, mContext);
boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
// If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
if (largeScreenChanged && updateNavbarForTaskbar()) {
@@ -237,25 +240,28 @@
});
}
- /**
- * @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
@@ -266,7 +272,7 @@
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
- mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mIsTablet = isTablet(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -302,7 +308,7 @@
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
- if (updateNavbarForTaskbar()) {
+ if (initializeTaskbarIfNecessary()) {
return;
}
@@ -326,7 +332,7 @@
return;
}
- if (isThreeButtonTaskbarEnabled()) {
+ if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
return;
}
@@ -371,6 +377,7 @@
mHandler,
mNavBarOverlayController,
mUiEventLogger,
+ mNavigationBarA11yHelper,
mUserTracker);
mNavigationBars.put(displayId, navBar);
@@ -462,24 +469,6 @@
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),
- (int) (density* newConfig.screenHeightDp));
- DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
- float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- return (size / densityRatio) >= TABLET_MIN_DPS;
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
for (int i = 0; i < mNavigationBars.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 649ac87..a5b7911 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.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 @@
// 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 @@
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 @@
}
}
- 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 03147d8..1d44146 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -16,23 +16,100 @@
package com.android.systemui.navigationbar;
-import android.os.IBinder;
+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 android.view.InsetsState;
+
+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,
+ InsetsState requestedState, String packageName) {
+ 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 cb0c411..5325943 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_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 @@
}
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 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 @@
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/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8e52b0d..c7f8dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -50,6 +50,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -337,7 +338,7 @@
*/
default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) { }
+ @Behavior int behavior, InsetsState requestedState, String packageName) { }
/**
* @see IStatusBar#showTransient(int, int[]).
@@ -996,7 +997,7 @@
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.argi1 = displayId;
@@ -1004,7 +1005,8 @@
args.argi3 = navbarColorManagedByIme ? 1 : 0;
args.arg1 = appearanceRegions;
args.argi4 = behavior;
- args.argi5 = isFullscreen ? 1 : 0;
+ args.arg2 = requestedState;
+ args.arg3 = packageName;
mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget();
}
}
@@ -1387,7 +1389,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2,
(AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4,
- args.argi5 == 1);
+ (InsetsState) args.arg2, (String) args.arg3);
}
args.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 0725bf9..d4f5bd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
@@ -23,10 +27,16 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.os.SystemProperties;
import android.text.format.DateFormat;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.InsetsFlags;
+import android.view.InsetsState;
import android.view.View;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
@@ -56,6 +66,9 @@
public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
CallbackController<StateListener>, Dumpable {
private static final String TAG = "SbStateController";
+ private static final boolean DEBUG_IMMERSIVE_APPS =
+ SystemProperties.getBoolean("persist.debug.immersive_apps", false);
+
// Must be a power of 2
private static final int HISTORY_SIZE = 32;
@@ -420,7 +433,10 @@
}
@Override
- public void setFullscreenState(boolean isFullscreen) {
+ public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+ InsetsState requestedState, String packageName) {
+ boolean isFullscreen = !requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ || !requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR);
if (mIsFullscreen != isFullscreen) {
mIsFullscreen = isFullscreen;
synchronized (mListeners) {
@@ -429,6 +445,19 @@
}
}
}
+
+ // TODO (b/190543382): Finish the logging logic.
+ // This section can be removed if we don't need to print it on logcat.
+ if (DEBUG_IMMERSIVE_APPS) {
+ boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
+ String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
+ String requestedVisibilityString = requestedState.toSourceVisibilityString();
+ if (requestedVisibilityString.isEmpty()) {
+ requestedVisibilityString = "none";
+ }
+ Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
+ + " requested visibilities: " + requestedVisibilityString);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 2520050..0bbae2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -19,7 +19,10 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.view.InsetsState;
import android.view.View;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -155,9 +158,10 @@
boolean isKeyguardRequested();
/**
- * Set the fullscreen state
+ * Set the system bar attributes
*/
- void setFullscreenState(boolean isFullscreen);
+ void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+ InsetsState requestedState, String packageName);
/**
* Set pulsing
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 4ad7202..f22c139 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
@@ -112,7 +112,6 @@
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.leak.RotationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -949,7 +948,7 @@
return;
}
// Portrait is easy, just use the dimen for paddings
- if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) {
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mSidePaddings = mMinimumPaddings;
return;
}
@@ -5642,6 +5641,10 @@
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 09afedb..a92682a 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 @@
/**
* 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/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 7d13405..c213707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -21,6 +21,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
+import android.view.InsetsState;
import android.view.View;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -149,7 +150,7 @@
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
if (displayId != mDisplayId) {
return;
}
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 567318c..920e2a5 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.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.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;
@@ -629,6 +633,8 @@
private KeyguardMediaController mKeyguardMediaController;
+ private boolean mStatusViewCentered = false;
+
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -1036,16 +1042,16 @@
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();
}
@@ -1263,7 +1269,14 @@
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;
}
@@ -1284,7 +1297,12 @@
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 =
@@ -1328,6 +1346,26 @@
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
*/
@@ -2262,7 +2300,8 @@
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 022faf78..0ff6490 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 @@
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 cfcea96..44ed279 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 @@
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 @@
}
}
- 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 @@
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 @@
private void applyAndDispatchState() {
- applyStateToAlpha();
+ applyState();
if (mUpdatePending) {
return;
}
@@ -1203,6 +1206,7 @@
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/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 89711fa..d2a1750 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -104,6 +104,7 @@
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -984,7 +985,8 @@
showTransientUnchecked();
}
onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,
- result.mNavbarColorManagedByIme, result.mBehavior, result.mAppFullscreen);
+ result.mNavbarColorManagedByIme, result.mBehavior, result.mRequestedState,
+ result.mPackageName);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
@@ -2477,7 +2479,7 @@
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
if (displayId != mDisplayId) {
return;
}
@@ -2490,7 +2492,8 @@
mStatusBarMode, navbarColorManagedByIme);
updateBubblesVisibility();
- mStatusBarStateController.setFullscreenState(isFullscreen);
+ mStatusBarStateController.setSystemBarAttributes(
+ appearance, behavior, requestedState, packageName);
}
@Override
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 6f63b17..e009c0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -968,6 +968,9 @@
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 f33ff27..ac43b67 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 9a25a70..3d3b58a 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.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 @@
// 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 @@
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/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 1f1817c..5e70d0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -21,7 +21,7 @@
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
-import android.os.UserManager;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -76,7 +76,6 @@
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUserDetailAdapter mUserDetailAdapter;
private NotificationPanelViewController mNotificationPanelViewController;
- private UserManager mUserManager;
UserSwitcherController.UserRecord mCurrentUser;
// State info for the user switch and keyguard
@@ -115,7 +114,6 @@
UserAvatarView view,
Context context,
@Main Resources resources,
- UserManager userManager,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
KeyguardStateController keyguardStateController,
@@ -129,7 +127,6 @@
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
mContext = context;
mResources = resources;
- mUserManager = userManager;
mScreenLifecycle = screenLifecycle;
mUserSwitcherController = userSwitcherController;
mKeyguardStateController = keyguardStateController;
@@ -227,47 +224,39 @@
return;
}
- if (mCurrentUser == null) {
- mView.setVisibility(View.GONE);
- return;
- }
-
- mView.setVisibility(View.VISIBLE);
-
- String currentUserName = mCurrentUser.info.name;
String contentDescription = null;
-
- if (!TextUtils.isEmpty(currentUserName)) {
+ if (mCurrentUser != null && mCurrentUser.info != null && !TextUtils.isEmpty(
+ mCurrentUser.info.name)) {
+ // If we know the current user's name, have TalkBack to announce "Signed in as [user
+ // name]" when the icon is selected
+ contentDescription = mContext.getString(R.string.accessibility_quick_settings_user,
+ mCurrentUser.info.name);
+ } else {
+ // As a fallback, have TalkBack announce "Switch user"
contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_user,
- currentUserName);
+ R.string.accessibility_multi_user_switch_switcher);
}
if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) {
mView.setContentDescription(contentDescription);
}
- mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), mCurrentUser.resolveId());
+ int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL;
+ mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
}
Drawable getCurrentUserIcon() {
Drawable drawable;
- if (mCurrentUser.picture == null) {
- if (mCurrentUser.isCurrent && mCurrentUser.isGuest) {
+ if (mCurrentUser == null || mCurrentUser.picture == null) {
+ if (mCurrentUser != null && mCurrentUser.isGuest) {
drawable = mContext.getDrawable(R.drawable.ic_avatar_guest_user);
} else {
- drawable = mAdapter.getIconDrawable(mContext, mCurrentUser);
+ drawable = mContext.getDrawable(R.drawable.ic_avatar_user);
}
- int iconColorRes;
- if (mCurrentUser.isSwitchToEnabled) {
- iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
- } else {
- iconColorRes = R.color.kg_user_switcher_restricted_avatar_icon_color;
- }
+ int iconColorRes = R.color.kg_user_switcher_avatar_icon_color;
drawable.setTint(mResources.getColor(iconColorRes, mContext.getTheme()));
} else {
- int avatarSize =
- (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
+ int avatarSize = (int) mResources.getDimension(R.dimen.kg_framed_avatar_size);
drawable = new CircleFramedDrawable(mCurrentUser.picture, avatarSize);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 5de7846a..9446732 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 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 @@
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 @@
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 @@
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 2216a91..3be1d3c 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.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 @@
@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 @@
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 bc956dc..f2db4f1 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.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 @@
// 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 @@
private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
+ private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@@ -128,7 +131,8 @@
@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 @@
mScreenLifecycle = screenLifecycle;
mSysUiState = sysUiState;
mPipOptional = pipOptional;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
@@ -165,6 +170,7 @@
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 @@
}
@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 @@
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 6ef7450..7e733a9 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 @@
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 @@
@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 36fd9be..be7813e 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.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 @@
@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 @@
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 @@
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 10ed1d7..ce02b83 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 @@
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 @@
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 ca857c5..64bdc2e 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 @@
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.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.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 @@
@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 @@
private EmergencyButtonController mEmergencyButtonController;
@Mock
private Resources mResources;
+ @Mock
+ private FalsingCollector mFalsingCollector;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -112,7 +119,9 @@
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 @@
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController)
+ mConfigurationController, mFalsingCollector)
.create(mSecurityCallback);
}
@@ -169,18 +178,156 @@
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 f5916e7..0276323 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 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 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 @@
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 @@
}
@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 @@
}
@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 @@
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 @@
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 @@
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 9621bed..8bb9d42 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 @@
@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 @@
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 f62069d..76cc7a0e 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.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.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 @@
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 @@
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 @@
}
@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 @@
}
@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 578c2d9..32abbc7 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 @@
@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 @@
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 302a8d3..0000000
--- 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 d2527c6..e9a7c73 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 @@
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
+ private CommandQueue mCommandQueue = mock(CommandQueue.class);
+
private static final int SECONDARY_DISPLAY = 1;
@Before
@@ -99,7 +101,7 @@
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 @@
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
mock(ConfigurationController.class),
+ mock(NavigationBarA11yHelper.class),
+ mock(TaskbarDelegate.class),
mock(UserTracker.class)));
initializeNavigationBars();
}
@@ -275,4 +279,9 @@
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 eac68f6..b991976 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 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 @@
@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 @@
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 a570675..c606a43 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 @@
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
+ mock(NavigationBarA11yHelper.class),
mock(UserTracker.class)));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 21c6292..d5a2919 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
+import android.view.InsetsState;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -124,24 +125,25 @@
public void testOnSystemBarAttributesChanged() {
doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, false);
+ BEHAVIOR_DEFAULT, new InsetsState(), "test");
}
@Test
public void testOnSystemBarAttributesChangedForSecondaryDisplay() {
doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, false);
+ BEHAVIOR_DEFAULT, new InsetsState(), "test");
}
private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedState, packageName);
waitForIdleSync();
verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance),
- eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior), eq(isFullscreen));
+ eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior),
+ eq(requestedState), eq(packageName));
}
@Test
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
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f376e88..6ee2f20 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 @@
*/
@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 @@
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 @@
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/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index cdfab1e..c3adee9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -102,7 +102,8 @@
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
assertTrue(mLightsOutNotifController.areLightsOut());
}
@@ -114,7 +115,8 @@
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
assertFalse(mLightsOutNotifController.areLightsOut());
}
@@ -144,7 +146,8 @@
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
// THEN we should show dot
assertTrue(mLightsOutNotifController.shouldShowDot());
@@ -163,7 +166,8 @@
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
@@ -182,7 +186,8 @@
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedState */,
+ null /* packageName */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
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 a6fc02d..1540b14 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 @@
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.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 @@
}
@Test
- public void testKeyguardStatusView_isAlignedToGuidelineInSplitShadeMode() {
- mNotificationPanelViewController.updateResources();
-
- assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(ConstraintSet.PARENT_ID);
-
+ public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() {
+ mStatusBarStateController.setState(KEYGUARD);
enableSplitShade();
- mNotificationPanelViewController.updateResources();
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mNotificationPanelViewController.updateResources();
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
.isEqualTo(R.id.qs_edge_guideline);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ mNotificationPanelViewController.updateResources();
+ assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
+ .isEqualTo(ConstraintSet.PARENT_ID);
}
@Test
@@ -744,6 +749,38 @@
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 678b193..2685b76 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 @@
}
@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 f243077..d6492c6 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.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 @@
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 e4c7800..db8a08c 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.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 @@
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 cd5aa9a..7b77cb0 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.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 @@
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 5691660..8480702 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.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 @@
@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 @@
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 @@
}
@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 e9c9899..ee80dae 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_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 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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
*/
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 @@
*/
@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 @@
*/
@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 @@
// 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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 7403af7..7d2b71f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,7 +19,9 @@
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.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.os.PowerManager;
@@ -43,7 +45,10 @@
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.StringJoiner;
/**
* This class is an input filter for implementing accessibility features such
@@ -171,9 +176,9 @@
private int mEnabledFeatures;
- private EventStreamState mMouseStreamState;
+ private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);
- private EventStreamState mTouchScreenStreamState;
+ private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);
private EventStreamState mKeyboardStreamState;
@@ -211,10 +216,17 @@
super.onUninstalled();
}
- void onDisplayChanged() {
+ void onDisplayAdded(@NonNull Display display) {
if (mInstalled) {
- disableFeatures();
- enableFeatures();
+ resetStreamStateForDisplay(display.getDisplayId());
+ enableFeaturesForDisplay(display);
+ }
+ }
+
+ void onDisplayRemoved(int displayId) {
+ if (mInstalled) {
+ disableFeaturesForDisplay(displayId);
+ resetStreamStateForDisplay(displayId);
}
}
@@ -224,7 +236,12 @@
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);
@@ -237,16 +254,17 @@
return;
}
- int eventSource = event.getSource();
+ final int eventSource = event.getSource();
+ final int displayId = event.getDisplayId();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
state.reset();
- clearEventsForAllEventHandlers(eventSource);
+ clearEventStreamHandler(displayId, eventSource);
super.onInputEvent(event, policyFlags);
return;
}
if (state.updateInputSource(event.getSource())) {
- clearEventsForAllEventHandlers(eventSource);
+ clearEventStreamHandler(displayId, eventSource);
}
if (!state.inputSourceValid()) {
@@ -275,35 +293,39 @@
*/
private EventStreamState getEventStreamState(InputEvent event) {
if (event instanceof MotionEvent) {
- if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
- if (mTouchScreenStreamState == null) {
- mTouchScreenStreamState = new TouchScreenEventStreamState();
- }
- return mTouchScreenStreamState;
- }
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
- if (mMouseStreamState == null) {
- mMouseStreamState = new MouseEventStreamState();
- }
- return mMouseStreamState;
- }
+ final int displayId = event.getDisplayId();
+
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+ if (touchScreenStreamState == null) {
+ touchScreenStreamState = new TouchScreenEventStreamState();
+ mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
+ }
+ return touchScreenStreamState;
+ }
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+ if (mouseStreamState == null) {
+ mouseStreamState = new MouseEventStreamState();
+ mMouseStreamStates.put(displayId, mouseStreamState);
+ }
+ return mouseStreamState;
+ }
} else if (event instanceof KeyEvent) {
- if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
- if (mKeyboardStreamState == null) {
- mKeyboardStreamState = new KeyboardEventStreamState();
- }
- return mKeyboardStreamState;
- }
+ if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
+ if (mKeyboardStreamState == null) {
+ mKeyboardStreamState = new KeyboardEventStreamState();
+ }
+ return mKeyboardStreamState;
+ }
}
return null;
}
- private void clearEventsForAllEventHandlers(int eventSource) {
- for (int i = 0; i < mEventHandler.size(); i++) {
- final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
- if (eventHandler != null) {
- eventHandler.clearEvents(eventSource);
- }
+ private void clearEventStreamHandler(int displayId, int eventSource) {
+ final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
+ if (eventHandler != null) {
+ eventHandler.clearEvents(eventSource);
}
}
@@ -419,55 +441,69 @@
private void enableFeatures() {
if (DEBUG) Slog.i(TAG, "enableFeatures()");
- resetStreamState();
+ resetAllStreamState();
final ArrayList<Display> displaysList = mAms.getValidDisplayList();
- if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
- mAutoclickController = new AutoclickController(mContext, mUserId);
- addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
- }
-
for (int i = displaysList.size() - 1; i >= 0; i--) {
- final int displayId = displaysList.get(i).getDisplayId();
- final Context displayContext = mContext.createDisplayContext(displaysList.get(i));
+ enableFeaturesForDisplay(displaysList.get(i));
+ }
+ enableDisplayIndependentFeatures();
+ }
- if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
- TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
- if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
- explorer.setServiceHandlesDoubleTap(true);
- }
- if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
- explorer.setMultiFingerGesturesEnabled(true);
- }
- if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
- explorer.setTwoFingerPassthroughEnabled(true);
- }
- if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
- explorer.setSendMotionEventsEnabled(true);
- }
- addFirstEventHandler(displayId, explorer);
- mTouchExplorer.put(displayId, explorer);
- }
-
- if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
- || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
- final MagnificationGestureHandler magnificationGestureHandler =
- createMagnificationGestureHandler(displayId,
- displayContext);
- addFirstEventHandler(displayId, magnificationGestureHandler);
- mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
- }
-
- if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
- MotionEventInjector injector = new MotionEventInjector(
- mContext.getMainLooper());
- addFirstEventHandler(displayId, injector);
- mMotionEventInjectors.put(displayId, injector);
- }
+ private void enableFeaturesForDisplay(Display display) {
+ if (DEBUG) {
+ Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
}
+ final Context displayContext = mContext.createDisplayContext(display);
+ final int displayId = display.getDisplayId();
+
+ if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
+ if (mAutoclickController == null) {
+ mAutoclickController = new AutoclickController(
+ mContext, mUserId, mAms.getTraceManager());
+ }
+ addFirstEventHandler(displayId, mAutoclickController);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
+ if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
+ explorer.setServiceHandlesDoubleTap(true);
+ }
+ if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
+ explorer.setMultiFingerGesturesEnabled(true);
+ }
+ if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
+ explorer.setTwoFingerPassthroughEnabled(true);
+ }
+ if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
+ explorer.setSendMotionEventsEnabled(true);
+ }
+ addFirstEventHandler(displayId, explorer);
+ mTouchExplorer.put(displayId, explorer);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+ || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
+ final MagnificationGestureHandler magnificationGestureHandler =
+ createMagnificationGestureHandler(displayId,
+ displayContext);
+ addFirstEventHandler(displayId, magnificationGestureHandler);
+ mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+ MotionEventInjector injector = new MotionEventInjector(
+ mContext.getMainLooper(), mAms.getTraceManager());
+ addFirstEventHandler(displayId, injector);
+ mMotionEventInjectors.put(displayId, injector);
+ }
+ }
+
+ private void enableDisplayIndependentFeatures() {
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
mAms.setMotionEventInjectors(mMotionEventInjectors);
}
@@ -500,55 +536,57 @@
mEventHandler.put(displayId, eventHandler);
}
- /**
- * Adds an event handler to the event handler chain for all displays. The handler is added at
- * the beginning of the chain.
- *
- * @param displayList The list of displays
- * @param handler The handler to be added to the event handlers list.
- */
- private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
- EventStreamTransformation handler) {
- for (int i = 0; i < displayList.size(); i++) {
- final int displayId = displayList.get(i).getDisplayId();
- addFirstEventHandler(displayId, handler);
+ private void disableFeatures() {
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
+ }
+ mAms.setMotionEventInjectors(null);
+ disableDisplayIndependentFeatures();
+
+ resetAllStreamState();
+ }
+
+ private void disableFeaturesForDisplay(int displayId) {
+ if (DEBUG) {
+ Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
+ }
+
+ final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
+ if (injector != null) {
+ injector.onDestroy();
+ mMotionEventInjectors.remove(displayId);
+ }
+
+ final TouchExplorer explorer = mTouchExplorer.get(displayId);
+ if (explorer != null) {
+ explorer.onDestroy();
+ mTouchExplorer.remove(displayId);
+ }
+
+ final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
+ if (handler != null) {
+ handler.onDestroy();
+ mMagnificationGestureHandler.remove(displayId);
+ }
+
+ final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
+ if (eventStreamTransformation != null) {
+ mEventHandler.remove(displayId);
}
}
- private void disableFeatures() {
- for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
- final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
- if (injector != null) {
- injector.onDestroy();
- }
- }
- mAms.setMotionEventInjectors(null);
- mMotionEventInjectors.clear();
+ private void disableDisplayIndependentFeatures() {
if (mAutoclickController != null) {
mAutoclickController.onDestroy();
mAutoclickController = null;
}
- for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
- final TouchExplorer explorer = mTouchExplorer.valueAt(i);
- if (explorer != null) {
- explorer.onDestroy();
- }
- }
- mTouchExplorer.clear();
- for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
- final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);
- if (handler != null) {
- handler.onDestroy();
- }
- }
- mMagnificationGestureHandler.clear();
+
if (mKeyboardInterceptor != null) {
mKeyboardInterceptor.onDestroy();
mKeyboardInterceptor = null;
}
-
- mEventHandler.clear();
- resetStreamState();
}
private MagnificationGestureHandler createMagnificationGestureHandler(
@@ -563,32 +601,46 @@
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;
}
- void resetStreamState() {
- if (mTouchScreenStreamState != null) {
- mTouchScreenStreamState.reset();
+ void resetAllStreamState() {
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
}
- if (mMouseStreamState != null) {
- mMouseStreamState.reset();
- }
+
if (mKeyboardStreamState != null) {
mKeyboardStreamState.reset();
}
}
+ void resetStreamStateForDisplay(int displayId) {
+ final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+ if (touchScreenStreamState != null) {
+ touchScreenStreamState.reset();
+ mTouchScreenStreamStates.remove(displayId);
+ }
+
+ final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+ if (mouseStreamState != null) {
+ mouseStreamState.reset();
+ mMouseStreamStates.remove(displayId);
+ }
+ }
+
@Override
public void onDestroy() {
/* ignore */
@@ -839,4 +891,45 @@
mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
}
}
+
+ /**
+ * Dumps all {@link AccessibilityInputFilter}s here.
+ */
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (mEventHandler == null) {
+ return;
+ }
+ pw.append("A11yInputFilter Info : ");
+ pw.println();
+
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+ for (int i = 0; i < displaysList.size(); i++) {
+ final int displayId = displaysList.get(i).getDisplayId();
+ EventStreamTransformation next = mEventHandler.get(displayId);
+ if (next != null) {
+ pw.append("Enabled features of Display [");
+ pw.append(Integer.toString(displayId));
+ pw.append("] = ");
+
+ final StringJoiner joiner = new StringJoiner(",", "[", "]");
+
+ while (next != null) {
+ if (next instanceof MagnificationGestureHandler) {
+ joiner.add("MagnificationGesture");
+ } else if (next instanceof KeyboardInterceptor) {
+ joiner.add("KeyboardInterceptor");
+ } else if (next instanceof TouchExplorer) {
+ joiner.add("TouchExplorer");
+ } else if (next instanceof AutoclickController) {
+ joiner.add("AutoclickController");
+ } else if (next instanceof MotionEventInjector) {
+ joiner.add("MotionEventInjector");
+ }
+ next = next.getNext();
+ }
+ pw.append(joiner.toString());
+ }
+ pw.println();
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f631988..04ef101 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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
// 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 @@
@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 @@
@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 @@
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 @@
@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 @@
@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 @@
}
}
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 @@
*/
@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 @@
*/
@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 @@
@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 @@
@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 @@
@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 @@
}
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 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 @@
@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 @@
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
*/
@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 @@
*/
@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 @@
*/
@Override
public void onSystemActionsChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged");
- }
-
synchronized (mLock) {
AccessibilityUserState state = getCurrentUserStateLocked();
notifySystemActionsChangedLocked(state);
@@ -1256,11 +1272,6 @@
@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 @@
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 @@
private int getClientStateLocked(AccessibilityUserState userState) {
return userState.getClientStateLocked(
mUiAutomationManager.isUiAutomationRunningLocked(),
- mTraceManager.isA11yTracingEnabled());
+ mTraceManager.getTraceStateForAccessibilityManagerClientState());
}
private InteractionBridge getInteractionBridge() {
@@ -1681,6 +1696,10 @@
}
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 @@
@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 @@
}
}
- 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 @@
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 @@
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 @@
}
}
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 @@
@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 @@
@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 @@
*/
@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 @@
@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 @@
@Override
public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked",
- "event=" + event);
- }
-
sendAccessibilityEventLocked(event, mCurrentUserId);
}
@@ -3148,8 +3158,10 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
@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 @@
@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 @@
@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 @@
* 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 @@
* 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();
@@ -3314,6 +3338,9 @@
pw.println();
}
mA11yWindowManager.dump(fd, pw, args);
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.dump(fd, pw, args);
+ }
pw.println("Global client list info:{");
mGlobalClients.dump(pw, " Client list ");
pw.println(" Registered clients:{");
@@ -3350,9 +3377,6 @@
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController");
- }
synchronized (mLock) {
return mMagnificationController.getFullScreenMagnificationController();
}
@@ -3360,11 +3384,6 @@
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked",
- "serviceInfoChanged=" + serviceInfoChanged);
- }
-
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
@@ -3569,7 +3588,7 @@
synchronized (mLock) {
mDisplaysList.add(display);
if (mInputFilter != null) {
- mInputFilter.onDisplayChanged();
+ mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3591,7 +3610,7 @@
return;
}
if (mInputFilter != null) {
- mInputFilter.onDisplayChanged();
+ mInputFilter.onDisplayRemoved(displayId);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3891,11 +3910,6 @@
@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 +3920,6 @@
@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 +3948,10 @@
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 +3961,7 @@
}
- 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 7d75b73..467cab5 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 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 @@
*/
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 @@
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
}
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 @@
}
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 @@
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 6396960..8cf5547 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 @@
}
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 0391413..0000000
--- 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 6105e8a..51e01ea 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;
- AccessibilityTraceManager(
+ private long mEnabledLoggingFlags;
+
+ private static AccessibilityTraceManager sInstance = null;
+
+ @MainThread
+ static AccessibilityTraceManager getInstance(
@NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
- @NonNull AccessibilityManagerService service) {
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityTraceManager(a11yController, service, lock);
+ }
+ return sInstance;
+ }
+
+ private AccessibilityTraceManager(
+ @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
+ @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 void startTrace() {
- if (!mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.startTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+ 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(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());
- }
-
- @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(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")));
}
}
- int onShellCommand(String cmd) {
+ @Override
+ 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));
+ }
+ }
+
+ 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 @@
}
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 0fde0de..c70bf73 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 @@
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 @@
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 ff79469..b05dffe 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 @@
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 @@
// 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 @@
*/
void stopTrackingWindowsLocked() {
if (mTrackingWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=null");
+ }
mWindowManagerInternal.setWindowsForAccessibilityCallback(
mDisplayId, null);
mTrackingWindows = false;
@@ -373,6 +384,20 @@
}
}
+ /**
+ * 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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
* @return The userId
*/
public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
+ if (traceWMEnabled()) {
+ logTraceWM("getWindowOwnerUserId", "token=" + windowToken);
+ }
return mWindowManagerInternal.getWindowOwnerUserId(windowToken);
}
@@ -1547,6 +1590,10 @@
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 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 @@
* @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 @@
return;
}
}
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
try {
connection.getRemote().clearAccessibilityFocus();
} catch (RemoteException re) {
@@ -1666,6 +1722,25 @@
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 f5b0eb1..95f3560 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 @@
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 @@
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 @@
@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 bc379c2..b8250c0 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 @@
@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 2673cd1..5cbd1a2 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 @@
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 @@
/**
* @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 @@
@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 9547280..6cd23fc 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 @@
// 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 e1af2c4..f95de4e 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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 1f66bfd..718da9e 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.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 @@
*/
@GuardedBy("mLock")
boolean register() {
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=" + this);
+ }
mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, this);
if (!mRegistered) {
@@ -142,6 +149,10 @@
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 @@
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 @@
}
@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 @@
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 @@
}
}
+ 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 @@
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 @@
}
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 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 @@
long animationDuration) {
mContext = context;
mAms = ams;
+ mTrace = ams.getTraceManager();
mWindowManager = windowManager;
mHandler = handler;
mAnimationDuration = animationDuration;
@@ -1491,6 +1536,14 @@
}
/**
+ * @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 f7d1b9a..c3d8d4c 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 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 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 f9aecd7..8aacafb 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 @@
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 bbe40b6..19b3396 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.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 @@
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 @@
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 993027d..5277425 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.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
/**
* A wrapper of {@link IWindowMagnificationConnection}.
*/
@@ -36,9 +41,12 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 4fb9a03..b26d364 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.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 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 938cb73..7a111d8 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.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 @@
}
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 @@
mConnectionWrapper = null;
}
if (connection != null) {
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
}
if (mConnectionWrapper != null) {
@@ -197,7 +204,10 @@
}
}
}
-
+ 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 @@
@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 @@
@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 @@
@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 55b982b..cbc3238 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -107,6 +107,7 @@
":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 b2d35f4..ed7d185 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.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.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 @@
}
};
+ private final FoldStateListener mFoldStateListener;
+
public CameraServiceProxy(Context context) {
super(context);
mContext = context;
@@ -459,6 +464,14 @@
// 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 @@
*
* @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 @@
*
* @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 @@
} 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/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
new file mode 100644
index 0000000..a81213d
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -0,0 +1,383 @@
+/*
+ * 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.compat.overrides;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A utility class for parsing App Compat Overrides flags.
+ *
+ * @hide
+ */
+final class AppCompatOverridesParser {
+ /**
+ * Flag for specifying all compat change IDs owned by a namespace. See {@link
+ * #parseOwnedChangeIds} for information on how this flag is parsed.
+ */
+ static final String FLAG_OWNED_CHANGE_IDS = "owned_change_ids";
+
+ /**
+ * Flag for immediately removing overrides for certain packages and change IDs (from the compat
+ * platform), as well as stopping to apply them, in case of an emergency. See {@link
+ * #parseRemoveOverrides} for information on how this flag is parsed.
+ */
+ static final String FLAG_REMOVE_OVERRIDES = "remove_overrides";
+
+ private static final String TAG = "AppCompatOverridesParser";
+
+ private static final String WILDCARD_SYMBOL = "*";
+
+ private static final Pattern BOOLEAN_PATTERN =
+ Pattern.compile("true|false", Pattern.CASE_INSENSITIVE);
+
+ private static final String WILDCARD_NO_OWNED_CHANGE_IDS_WARNING =
+ "Wildcard can't be used in '" + FLAG_REMOVE_OVERRIDES + "' flag with an empty "
+ + FLAG_OWNED_CHANGE_IDS + "' flag";
+
+ private final PackageManager mPackageManager;
+
+ AppCompatOverridesParser(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Parses the given {@code configStr} and returns a map from package name to a set of change
+ * IDs to remove for that package.
+ *
+ * <p>The given {@code configStr} is expected to either be:
+ *
+ * <ul>
+ * <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+ * ownedChangeIds}, for all installed packages should be removed.
+ * <li>A comma separated key value list, where the key is a package name and the value is
+ * either:
+ * <ul>
+ * <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+ * ownedChangeIds} for that package should be removed.
+ * <li>A colon separated list of change IDs to remove for that package.
+ * </ul>
+ * </ul>
+ *
+ * <p>If the given {@code configStr} doesn't match the expected format, an empty map will be
+ * returned. If a specific change ID isn't a valid long, it will be ignored.
+ */
+ Map<String, Set<Long>> parseRemoveOverrides(String configStr, Set<Long> ownedChangeIds) {
+ if (configStr.isEmpty()) {
+ return emptyMap();
+ }
+
+ Map<String, Set<Long>> result = new ArrayMap<>();
+ if (configStr.equals(WILDCARD_SYMBOL)) {
+ if (ownedChangeIds.isEmpty()) {
+ Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+ return emptyMap();
+ }
+ List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(
+ MATCH_ANY_USER);
+ for (ApplicationInfo appInfo : installedApps) {
+ result.put(appInfo.packageName, ownedChangeIds);
+ }
+ return result;
+ }
+
+ KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(configStr);
+ } catch (IllegalArgumentException e) {
+ Slog.w(
+ TAG,
+ "Invalid format in '" + FLAG_REMOVE_OVERRIDES + "' flag: " + configStr, e);
+ return emptyMap();
+ }
+ for (int i = 0; i < parser.size(); i++) {
+ String packageName = parser.keyAt(i);
+ String changeIdsStr = parser.getString(packageName, /* def= */ "");
+ if (changeIdsStr.equals(WILDCARD_SYMBOL)) {
+ if (ownedChangeIds.isEmpty()) {
+ Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+ continue;
+ }
+ result.put(packageName, ownedChangeIds);
+ } else {
+ for (String changeIdStr : changeIdsStr.split(":")) {
+ try {
+ long changeId = Long.parseLong(changeIdStr);
+ result.computeIfAbsent(packageName, k -> new ArraySet<>()).add(changeId);
+ } catch (NumberFormatException e) {
+ Slog.w(
+ TAG,
+ "Invalid change ID in '" + FLAG_REMOVE_OVERRIDES + "' flag: "
+ + changeIdStr, e);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Parses the given {@code configStr}, that is expected to be a comma separated list of change
+ * IDs, into a set.
+ *
+ * <p>If any of the change IDs isn't a valid long, it will be ignored.
+ */
+ static Set<Long> parseOwnedChangeIds(String configStr) {
+ if (configStr.isEmpty()) {
+ return emptySet();
+ }
+
+ Set<Long> result = new ArraySet<>();
+ for (String changeIdStr : configStr.split(",")) {
+ try {
+ result.add(Long.parseLong(changeIdStr));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG,
+ "Invalid change ID in '" + FLAG_OWNED_CHANGE_IDS + "' flag: " + changeIdStr,
+ e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parses the given {@code configStr}, that is expected to be a comma separated list of changes
+ * overrides, and returns a {@link PackageOverrides}.
+ *
+ * <p>Each change override is in the following format:
+ * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled?>'. If <enabled> is empty,
+ * this indicates that any override for the specified change ID should be removed.
+ *
+ * <p>If there are multiple overrides that should be added with the same change ID, the one
+ * that best fits the given {@code versionCode} is added.
+ *
+ * <p>Any overrides whose change ID is in {@code changeIdsToSkip} are ignored.
+ *
+ * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. If the
+ * same change ID is both added and removed, i.e., has a change override entry with an empty
+ * enabled and another with a non-empty enabled, the change ID will only be removed.
+ */
+ static PackageOverrides parsePackageOverrides(
+ String configStr, long versionCode, Set<Long> changeIdsToSkip) {
+ if (configStr.isEmpty()) {
+ return new PackageOverrides();
+ }
+ PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode);
+ Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>();
+ Set<Long> overridesToRemove = new ArraySet<>();
+ for (String overrideEntryString : configStr.split(",")) {
+ List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4));
+ if (changeIdAndVersions.size() != 4) {
+ Slog.w(TAG, "Invalid change override entry: " + overrideEntryString);
+ continue;
+ }
+ long changeId;
+ try {
+ changeId = Long.parseLong(changeIdAndVersions.get(0));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid change ID in override entry: " + overrideEntryString, e);
+ continue;
+ }
+
+ if (changeIdsToSkip.contains(changeId)) {
+ continue;
+ }
+
+ String minVersionCodeStr = changeIdAndVersions.get(1);
+ String maxVersionCodeStr = changeIdAndVersions.get(2);
+
+ String enabledStr = changeIdAndVersions.get(3);
+ if (enabledStr.isEmpty()) {
+ if (!minVersionCodeStr.isEmpty() || !maxVersionCodeStr.isEmpty()) {
+ Slog.w(
+ TAG,
+ "min/max version code should be empty if enabled is empty: "
+ + overrideEntryString);
+ }
+ overridesToRemove.add(changeId);
+ continue;
+ }
+ if (!BOOLEAN_PATTERN.matcher(enabledStr).matches()) {
+ Slog.w(TAG, "Invalid enabled string in override entry: " + overrideEntryString);
+ continue;
+ }
+ boolean enabled = Boolean.parseBoolean(enabledStr);
+ PackageOverride.Builder overrideBuilder = new PackageOverride.Builder().setEnabled(
+ enabled);
+ try {
+ if (!minVersionCodeStr.isEmpty()) {
+ overrideBuilder.setMinVersionCode(Long.parseLong(minVersionCodeStr));
+ }
+ if (!maxVersionCodeStr.isEmpty()) {
+ overrideBuilder.setMaxVersionCode(Long.parseLong(maxVersionCodeStr));
+ }
+ } catch (NumberFormatException e) {
+ Slog.w(TAG,
+ "Invalid min/max version code in override entry: " + overrideEntryString,
+ e);
+ continue;
+ }
+
+ try {
+ PackageOverride override = overrideBuilder.build();
+ if (!overridesToAdd.containsKey(changeId)
+ || comparator.compare(override, overridesToAdd.get(changeId)) < 0) {
+ overridesToAdd.put(changeId, override);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed to build PackageOverride", e);
+ }
+ }
+
+ for (Long changeId : overridesToRemove) {
+ if (overridesToAdd.containsKey(changeId)) {
+ Slog.w(
+ TAG,
+ "Change ID ["
+ + changeId
+ + "] is both added and removed in package override flag: "
+ + configStr);
+ overridesToAdd.remove(changeId);
+ }
+ }
+
+ return new PackageOverrides(overridesToAdd, overridesToRemove);
+ }
+
+ /**
+ * A container for a map from change ID to {@link PackageOverride} to add and a set of change
+ * IDs to remove overrides for.
+ *
+ * <p>The map of overrides to add and the set of overrides to remove are mutually exclusive.
+ */
+ static final class PackageOverrides {
+ public final Map<Long, PackageOverride> overridesToAdd;
+ public final Set<Long> overridesToRemove;
+
+ PackageOverrides() {
+ this(emptyMap(), emptySet());
+ }
+
+ PackageOverrides(Map<Long, PackageOverride> overridesToAdd, Set<Long> overridesToRemove) {
+ this.overridesToAdd = overridesToAdd;
+ this.overridesToRemove = overridesToRemove;
+ }
+ }
+
+ /**
+ * A {@link Comparator} that compares @link PackageOverride} instances with respect to a
+ * specified {@code versionCode} as follows:
+ *
+ * <ul>
+ * <li>Prefer the {@link PackageOverride} whose version range contains {@code versionCode}.
+ * <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+ * versionCode} from below.
+ * <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+ * versionCode} from above.
+ * </ul>
+ */
+ private static final class PackageOverrideComparator implements Comparator<PackageOverride> {
+ private final long mVersionCode;
+
+ PackageOverrideComparator(long versionCode) {
+ this.mVersionCode = versionCode;
+ }
+
+ @Override
+ public int compare(PackageOverride o1, PackageOverride o2) {
+ // Prefer overrides whose version range contains versionCode.
+ boolean isVersionInRange1 = isVersionInRange(o1, mVersionCode);
+ boolean isVersionInRange2 = isVersionInRange(o2, mVersionCode);
+ if (isVersionInRange1 != isVersionInRange2) {
+ return isVersionInRange1 ? -1 : 1;
+ }
+
+ // Otherwise, prefer overrides whose version range is before versionCode.
+ boolean isVersionAfterRange1 = isVersionAfterRange(o1, mVersionCode);
+ boolean isVersionAfterRange2 = isVersionAfterRange(o2, mVersionCode);
+ if (isVersionAfterRange1 != isVersionAfterRange2) {
+ return isVersionAfterRange1 ? -1 : 1;
+ }
+
+ // If both overrides' version ranges are either before or after versionCode, prefer
+ // those whose version range is closer to versionCode.
+ return Long.compare(
+ getVersionProximity(o1, mVersionCode), getVersionProximity(o2, mVersionCode));
+ }
+
+ /**
+ * Returns true if the version range in the given {@code override} contains {@code
+ * versionCode}.
+ */
+ private static boolean isVersionInRange(PackageOverride override, long versionCode) {
+ return override.getMinVersionCode() <= versionCode
+ && versionCode <= override.getMaxVersionCode();
+ }
+
+ /**
+ * Returns true if the given {@code versionCode} is strictly after the version range in the
+ * given {@code override}.
+ */
+ private static boolean isVersionAfterRange(PackageOverride override, long versionCode) {
+ return override.getMaxVersionCode() < versionCode;
+ }
+
+ /**
+ * Returns true if the given {@code versionCode} is strictly before the version range in the
+ * given {@code override}.
+ */
+ private static boolean isVersionBeforeRange(PackageOverride override, long versionCode) {
+ return override.getMinVersionCode() > versionCode;
+ }
+
+ /**
+ * In case the given {@code versionCode} is strictly before or after the version range in
+ * the given {@code override}, returns the distance from it, otherwise returns zero.
+ */
+ private static long getVersionProximity(PackageOverride override, long versionCode) {
+ if (isVersionAfterRange(override, versionCode)) {
+ return versionCode - override.getMaxVersionCode();
+ }
+ if (isVersionBeforeRange(override, versionCode)) {
+ return override.getMinVersionCode() - versionCode;
+ }
+
+ // Version is in range. Note that when two overrides have a zero version proximity
+ // they will be ordered arbitrarily.
+ return 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
new file mode 100644
index 0000000..a81b18b
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -0,0 +1,279 @@
+/*
+ * 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.compat.overrides;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.provider.DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES;
+
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.SystemService;
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service for applying per-app compat overrides delivered via Device Config.
+ *
+ * <p>The service listens both on changes to supported Device Config namespaces and on package
+ * added/changed/removed events, and applies overrides accordingly.
+ *
+ * @hide
+ */
+public final class AppCompatOverridesService {
+ private static final String TAG = "AppCompatOverridesService";
+
+ private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(
+ NAMESPACE_APP_COMPAT_OVERRIDES);
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final IPlatformCompat mPlatformCompat;
+ private final List<String> mSupportedNamespaces;
+ private final List<DeviceConfigListener> mDeviceConfigListeners;
+ private final AppCompatOverridesParser mOverridesParser;
+
+ private AppCompatOverridesService(Context context) {
+ this(context, IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)), SUPPORTED_NAMESPACES);
+ }
+
+ @VisibleForTesting
+ AppCompatOverridesService(Context context, IPlatformCompat platformCompat,
+ List<String> supportedNamespaces) {
+ mContext = context;
+ mPackageManager = mContext.getPackageManager();
+ mPlatformCompat = platformCompat;
+ mSupportedNamespaces = supportedNamespaces;
+ mDeviceConfigListeners = new ArrayList<>();
+ mOverridesParser = new AppCompatOverridesParser(mPackageManager);
+ }
+
+ @Override
+ public void finalize() {
+ unregisterDeviceConfigListeners();
+ }
+
+ @VisibleForTesting
+ void registerDeviceConfigListeners() {
+ for (String namespace : mSupportedNamespaces) {
+ DeviceConfigListener listener = new DeviceConfigListener(namespace);
+ DeviceConfig.addOnPropertiesChangedListener(namespace, mContext.getMainExecutor(),
+ listener);
+ mDeviceConfigListeners.add(listener);
+ }
+ }
+
+ private void unregisterDeviceConfigListeners() {
+ for (DeviceConfigListener listener : mDeviceConfigListeners) {
+ DeviceConfig.removeOnPropertiesChangedListener(listener);
+ }
+ }
+
+ /**
+ * Same as {@link #applyOverrides(Properties, Map)} except all properties of the given {@code
+ * namespace} are fetched via {@link DeviceConfig#getProperties}.
+ */
+ private void applyAllOverrides(String namespace,
+ Map<String, Set<Long>> packageToChangeIdsToSkip) {
+ applyOverrides(DeviceConfig.getProperties(namespace), packageToChangeIdsToSkip);
+ }
+
+ /**
+ * Iterates all package override flags in the given {@code properties}, and for each flag whose
+ * package is installed on the device, parses its value and applies the overrides in it with
+ * respect to the package's current installed version.
+ */
+ private void applyOverrides(Properties properties,
+ Map<String, Set<Long>> packageToChangeIdsToSkip) {
+ Set<String> packageNames = new ArraySet<>(properties.getKeyset());
+ packageNames.remove(FLAG_OWNED_CHANGE_IDS);
+ packageNames.remove(FLAG_REMOVE_OVERRIDES);
+ for (String packageName : packageNames) {
+ Long versionCode = getVersionCodeOrNull(packageName);
+ if (versionCode == null) {
+ // Package isn't installed yet.
+ continue;
+ }
+
+ applyPackageOverrides(properties.getString(packageName, /* defaultValue= */ ""),
+ packageName, versionCode,
+ packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()));
+ }
+ }
+
+ /**
+ * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments, adds the
+ * resulting {@link PackageOverrides#overridesToAdd} via {@link
+ * IPlatformCompat#putOverridesOnReleaseBuilds}, and removes the resulting {@link
+ * PackageOverrides#overridesToRemove} via {@link
+ * IPlatformCompat#removeOverridesOnReleaseBuilds}.
+ */
+ private void applyPackageOverrides(String configStr, String packageName,
+ long versionCode, Set<Long> changeIdsToSkip) {
+ PackageOverrides packageOverrides = AppCompatOverridesParser.parsePackageOverrides(
+ configStr, versionCode, changeIdsToSkip);
+ putPackageOverrides(packageName, packageOverrides.overridesToAdd);
+ removePackageOverrides(packageName, packageOverrides.overridesToRemove);
+ }
+
+ /**
+ * Calls {@link IPlatformCompat#removeOverridesOnReleaseBuilds} on each package name and
+ * respective change IDs in {@code overridesToRemove}.
+ */
+ private void removeOverrides(Map<String, Set<Long>> overridesToRemove) {
+ for (Map.Entry<String, Set<Long>> packageNameAndOverrides : overridesToRemove.entrySet()) {
+ removePackageOverrides(packageNameAndOverrides.getKey(),
+ packageNameAndOverrides.getValue());
+ }
+ }
+
+ /**
+ * Fetches the value of {@link AppCompatOverridesParser#FLAG_REMOVE_OVERRIDES} for the given
+ * {@code namespace} and parses it into a map from package name to a set of change IDs to
+ * remove for that package.
+ */
+ private Map<String, Set<Long>> getOverridesToRemove(String namespace) {
+ return mOverridesParser.parseRemoveOverrides(
+ DeviceConfig.getString(namespace, FLAG_REMOVE_OVERRIDES, /* defaultValue= */ ""),
+ getOwnedChangeIds(namespace));
+ }
+
+ /**
+ * Fetches the value of {@link AppCompatOverridesParser#FLAG_OWNED_CHANGE_IDS} for the given
+ * {@code namespace} and parses it into a set of change IDs.
+ */
+ private static Set<Long> getOwnedChangeIds(String namespace) {
+ return AppCompatOverridesParser.parseOwnedChangeIds(
+ DeviceConfig.getString(namespace, FLAG_OWNED_CHANGE_IDS, /* defaultValue= */ ""));
+ }
+
+ private void putPackageOverrides(String packageName,
+ Map<Long, PackageOverride> overridesToAdd) {
+ if (overridesToAdd.isEmpty()) {
+ return;
+ }
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overridesToAdd);
+ try {
+ mPlatformCompat.putOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
+ }
+ }
+
+ private void removePackageOverrides(String packageName, Set<Long> overridesToRemove) {
+ if (overridesToRemove.isEmpty()) {
+ return;
+ }
+ CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+ overridesToRemove);
+ try {
+ mPlatformCompat.removeOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IPlatformCompat#removeOverridesOnReleaseBuilds", e);
+ }
+ }
+
+ @Nullable
+ private Long getVersionCodeOrNull(String packageName) {
+ try {
+ ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName,
+ MATCH_ANY_USER);
+ return applicationInfo.longVersionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package isn't installed yet.
+ return null;
+ }
+ }
+
+ /**
+ * SystemService lifecycle for AppCompatOverridesService.
+ *
+ * @hide
+ */
+ public static final class Lifecycle extends SystemService {
+ private AppCompatOverridesService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new AppCompatOverridesService(getContext());
+ mService.registerDeviceConfigListeners();
+ }
+ }
+
+ /**
+ * A {@link DeviceConfig.OnPropertiesChangedListener} that listens on changes to a given
+ * namespace and adds/removes overrides according to the changed flags.
+ */
+ private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+ private final String mNamespace;
+
+ private DeviceConfigListener(String namespace) {
+ mNamespace = namespace;
+ }
+
+ @Override
+ public void onPropertiesChanged(Properties properties) {
+ boolean removeOverridesFlagChanged = properties.getKeyset().contains(
+ FLAG_REMOVE_OVERRIDES);
+ boolean ownedChangedIdsFlagChanged = properties.getKeyset().contains(
+ FLAG_OWNED_CHANGE_IDS);
+
+ Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace);
+ if (removeOverridesFlagChanged || ownedChangedIdsFlagChanged) {
+ // In both cases it's possible that overrides that weren't removed before should
+ // now be removed.
+ removeOverrides(overridesToRemove);
+ }
+
+ if (removeOverridesFlagChanged) {
+ // We need to re-apply all overrides in the namespace since the remove overrides
+ // flag might have blocked some of them from being applied before.
+ applyAllOverrides(mNamespace, overridesToRemove);
+ } else {
+ applyOverrides(properties, overridesToRemove);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/compat/overrides/TEST_MAPPING b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
new file mode 100644
index 0000000..4b8f08e
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.compat.overrides"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 35f2957..9f806af 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 @@
// 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 @@
}
/**
+ * 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 @@
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 9acb4c8..5186744 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 @@
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 fd0f1c3..7955ecac 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 @@
}
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 @@
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 f47aa48..0a69aec 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 @@
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 @@
* @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 @@
* @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 3c9b106..04b5005 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.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 @@
* 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 @@
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 @@
}
}
- 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 @@
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 @@
}
return new DisplayFoldController(context, windowManagerService, displayService,
- cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler());
+ displayId, foldedArea, DisplayThread.getHandler());
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a436e6b..d95e826 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -132,7 +133,7 @@
/** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */
void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen);
+ @Behavior int behavior, InsetsState requestedState, String packageName);
/** @see com.android.internal.statusbar.IStatusBar#showTransient */
void showTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3a7e13b..47fdc4e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -59,6 +59,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -526,13 +527,13 @@
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
getUiState(displayId).setBarAttributes(appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedState, packageName);
if (mBar != null) {
try {
mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedState, packageName);
} catch (RemoteException ex) { }
}
}
@@ -1103,13 +1104,14 @@
return state;
}
- private class UiState {
+ private static class UiState {
private @Appearance int mAppearance = 0;
private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
- private ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
+ private final ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
private boolean mNavbarColorManagedByIme = false;
private @Behavior int mBehavior;
- private boolean mFullscreen = false;
+ private InsetsState mRequestedState = new InsetsState();
+ private String mPackageName = "none";
private int mDisabled1 = 0;
private int mDisabled2 = 0;
private int mImeWindowVis = 0;
@@ -1119,12 +1121,13 @@
private void setBarAttributes(@Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsState requestedState, String packageName) {
mAppearance = appearance;
mAppearanceRegions = appearanceRegions;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
- mFullscreen = isFullscreen;
+ mRequestedState = requestedState;
+ mPackageName = packageName;
}
private void showTransient(@InternalInsetsType int[] types) {
@@ -1244,8 +1247,8 @@
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
- state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen,
- transientBarTypes);
+ state.mNavbarColorManagedByIme, state.mBehavior, state.mRequestedState,
+ state.mPackageName, transientBarTypes);
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a24319f..54d97ee 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_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_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 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 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 @@
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 @@
// 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 @@
/**
* 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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
}
}
- 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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
+ "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
}
- private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
+ private void removeObserversForEmbeddedChildDisplays(WindowsForAccessibilityObserver
observerOfParentDisplay) {
final IntArray embeddedDisplayIdList =
observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
@@ -580,7 +609,7 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
}
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 @@
}
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 @@
}
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 @@
}
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 @@
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 @@
}
void recomputeBounds() {
- mDisplay.getRealSize(mScreenSize);
+ getDisplaySizeLocked(mScreenSize);
final int screenWidth = mScreenSize.x;
final int screenHeight = mScreenSize.y;
@@ -1052,9 +1090,10 @@
|| 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 @@
}, 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 @@
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 @@
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 @@
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 @@
private final Handler mHandler;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowsForAccessibilityCallback mCallback;
@@ -1502,24 +1546,26 @@
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 @@
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 @@
* @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 @@
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 @@
}
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 @@
return mTracing.isEnabled();
}
- @Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+ 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[] 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[] stackTrace, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
+ ignoreStackEntries);
+ }
+
+ @Override
+ 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 @@
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 @@
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,86 +2139,127 @@
/**
* 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();
}
@@ -2158,19 +2267,22 @@
/**
* 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 @@
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 @@
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 3a4faf7..8a76e3e5 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 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.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.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 @@
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 @@
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 0f6a718..b2e3fcb 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 @@
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 64b5fc7..5eba87d 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_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.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;
@@ -302,6 +302,7 @@
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;
@@ -315,12 +316,14 @@
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;
@@ -337,7 +340,6 @@
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;
@@ -346,6 +348,7 @@
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;
@@ -489,7 +492,7 @@
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
@@ -551,6 +554,21 @@
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.
*/
@@ -647,6 +665,14 @@
boolean allDrawn;
private boolean mLastAllDrawn;
+ /**
+ * Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this
+ * Actiivty was part of a syncset, all windows were ready by the time the sync was ready (vs.
+ * only the top-occluding ones). The assumption here is if some were not ready, they were
+ * covered with starting-window/splash-screen.
+ */
+ boolean mLastAllReadyAtSync = false;
+
private boolean mLastContainsShowWhenLockedWindow;
private boolean mLastContainsDismissKeyguardWindow;
private boolean mLastContainsTurnScreenOnWindow;
@@ -681,6 +707,8 @@
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
@@ -790,6 +818,7 @@
*/
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();
@@ -1135,6 +1164,76 @@
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;
}
@@ -1244,11 +1343,10 @@
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 */);
@@ -1271,31 +1369,25 @@
// 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);
@@ -1353,11 +1445,12 @@
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) {
@@ -2321,23 +2414,24 @@
}
/**
- * 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) {
@@ -2903,7 +2997,7 @@
}
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();
@@ -2982,12 +3076,12 @@
// 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");
}
@@ -3118,8 +3212,8 @@
// 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) {
@@ -3306,8 +3400,8 @@
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);
@@ -3386,7 +3480,7 @@
* 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.
@@ -3494,7 +3588,7 @@
// 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
@@ -4218,6 +4312,7 @@
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(
@@ -4227,11 +4322,17 @@
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(),
@@ -4243,6 +4344,9 @@
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(),
@@ -4258,6 +4362,8 @@
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(),
@@ -4297,6 +4403,7 @@
case ANIM_OPEN_CROSS_PROFILE_APPS:
displayContent.mAppTransition
.overridePendingAppTransitionStartCrossProfileApps();
+ options = AnimationOptions.makeCrossProfileAnimOptions();
break;
case ANIM_NONE:
case ANIM_UNDEFINED:
@@ -4305,6 +4412,10 @@
Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
}
+
+ if (options != null) {
+ mAtmService.getTransitionController().setOverrideAnimation(options);
+ }
}
void clearAllDrawn() {
@@ -4437,6 +4548,10 @@
}
}
+ 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
@@ -4601,7 +4716,7 @@
}
// 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
@@ -4850,7 +4965,7 @@
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);
@@ -4862,8 +4977,8 @@
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
@@ -4923,44 +5038,42 @@
}
}
- 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;
}
@@ -4968,8 +5081,8 @@
/**
* 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;
}
@@ -5173,13 +5286,6 @@
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:
@@ -5276,7 +5382,8 @@
*/
private boolean shouldBeResumed(ActivityRecord activeActivity) {
return shouldMakeActive(activeActivity) && isFocusable()
- && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE
+ && getTaskFragment().getVisibility(activeActivity)
+ == TASK_FRAGMENT_VISIBILITY_VISIBLE
&& canResumeByCompat();
}
@@ -5330,7 +5437,7 @@
if (!task.hasChild(this)) {
throw new IllegalStateException("Activity not found in its task");
}
- return task.topRunningActivity() == this;
+ return getTaskFragment().topRunningActivity() == this;
}
void handleAlreadyVisible() {
@@ -5419,16 +5526,17 @@
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();
}
@@ -6091,9 +6199,9 @@
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;
@@ -6171,7 +6279,8 @@
// 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;
@@ -6698,8 +6807,7 @@
}
final Configuration displayConfig = mDisplayContent.getConfiguration();
return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
- appRect, insets, thumbnailHeader, task, displayConfig.uiMode,
- displayConfig.orientation);
+ appRect, insets, thumbnailHeader, task, displayConfig.orientation);
}
@Override
@@ -7124,7 +7232,8 @@
// 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.
@@ -7239,7 +7348,7 @@
}
// Since bounds has changed, the configuration needs to be computed accordingly.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
/**
@@ -7255,8 +7364,53 @@
}
/**
- * 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.
@@ -7264,9 +7418,14 @@
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()) {
@@ -7286,7 +7445,8 @@
// 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;
}
@@ -7298,67 +7458,83 @@
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);
+ aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? letterboxAspectRatioOverride : aspectRatio;
- // Vertically center if orientation is landscape. Bounds will later be horizontally centered
- // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
+ // 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);
}
@@ -7382,11 +7558,13 @@
// 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());
}
}
@@ -7442,14 +7620,21 @@
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);
@@ -7508,7 +7693,6 @@
// 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) {
@@ -7593,6 +7777,10 @@
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;
@@ -7745,12 +7933,6 @@
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.
@@ -7759,19 +7941,17 @@
*/
// 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;
}
@@ -7779,30 +7959,20 @@
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:
@@ -7830,9 +8000,9 @@
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);
}
}
@@ -7856,13 +8026,6 @@
}
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;
}
@@ -8618,6 +8781,8 @@
* 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. */
@@ -8644,6 +8809,7 @@
/** 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();
@@ -8790,7 +8956,8 @@
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]);
}
}
}
@@ -8859,6 +9026,14 @@
return false;
}
+ @Override
+ void finishSync(Transaction outMergedTransaction, boolean cancel) {
+ // This override is just for getting metrics. allFinished needs to be checked before
+ // finish because finish resets all the states.
+ mLastAllReadyAtSync = allSyncFinished();
+ super.finishSync(outMergedTransaction, cancel);
+ }
+
static class Builder {
private final ActivityTaskManagerService mAtmService;
private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 8540fa7..30c7b23 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/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b6f2f24..d08d285 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -27,6 +27,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -38,6 +39,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -488,6 +490,16 @@
return START_SUCCESS;
}
+ void startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
+ @NonNull Intent activityIntent, @Nullable Bundle activityOptions) {
+ obtainStarter(activityIntent, "startActivityInTaskFragment")
+ .setActivityOptions(activityOptions)
+ .setInTaskFragment(taskFragment)
+ .setCallingUid(Binder.getCallingUid())
+ .setCallingPid(Binder.getCallingPid())
+ .execute();
+ }
+
void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e2ef82b..10c1f6b 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_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 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.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;
@@ -176,6 +177,7 @@
private int mPreferredWindowingMode;
private Task mInTask;
+ private TaskFragment mInTaskFragment;
@VisibleForTesting
boolean mAddingToTask;
private Task mReuseTask;
@@ -341,6 +343,7 @@
boolean avoidMoveToFront;
ActivityRecord[] outActivity;
Task inTask;
+ TaskFragment inTaskFragment;
String reason;
ProfilerInfo profilerInfo;
Configuration globalConfig;
@@ -391,6 +394,7 @@
componentSpecified = false;
outActivity = null;
inTask = null;
+ inTaskFragment = null;
reason = null;
profilerInfo = null;
globalConfig = null;
@@ -406,7 +410,7 @@
/**
* Adopts all values from passed in request.
*/
- void set(Request request) {
+ void set(@NonNull Request request) {
caller = request.caller;
intent = request.intent;
intentGrants = request.intentGrants;
@@ -431,6 +435,7 @@
componentSpecified = request.componentSpecified;
outActivity = request.outActivity;
inTask = request.inTask;
+ inTaskFragment = request.inTaskFragment;
reason = request.reason;
profilerInfo = request.profilerInfo;
globalConfig = request.globalConfig;
@@ -573,6 +578,7 @@
mPreferredWindowingMode = starter.mPreferredWindowingMode;
mInTask = starter.mInTask;
+ mInTaskFragment = starter.mInTaskFragment;
mAddingToTask = starter.mAddingToTask;
mReuseTask = starter.mReuseTask;
@@ -834,6 +840,7 @@
final int startFlags = request.startFlags;
final SafeActivityOptions options = request.activityOptions;
Task inTask = request.inTask;
+ mInTaskFragment = request.inTaskFragment;
int err = ActivityManager.START_SUCCESS;
// Pull the optional Ephemeral Installer-only bundle out of the options early.
@@ -1549,6 +1556,11 @@
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 +1592,8 @@
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 +1601,7 @@
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 +1663,6 @@
*
* 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 +1769,7 @@
mStartActivity.logStartActivity(
EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask());
- mTargetRootTask.mLastPausedActivity = null;
+ mStartActivity.getTaskFragment().clearLastPausedActivity();
mRootWindowContainer.startPowerModeLaunchIfNeeded(
false /* forceSend */, mStartActivity);
@@ -2034,7 +2046,7 @@
}
// For paranoia, make sure we have correctly resumed the top activity.
- topRootTask.mLastPausedActivity = null;
+ top.getTaskFragment().clearLastPausedActivity();
if (mDoResume) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -2130,7 +2142,7 @@
task.moveActivityToFrontLocked(act);
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
- mTargetRootTask.mLastPausedActivity = null;
+ act.getTaskFragment().clearLastPausedActivity();
} else {
mAddingToTask = true;
}
@@ -2198,6 +2210,7 @@
mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
mInTask = null;
+ mInTaskFragment = null;
mAddingToTask = false;
mReuseTask = null;
@@ -2558,7 +2571,7 @@
*/
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
@@ -2685,11 +2698,23 @@
mIntentDelivered = true;
}
- private void addOrReparentStartingActivity(Task parent, String reason) {
- if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
- parent.addChild(mStartActivity);
+ private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
+ TaskFragment newParent = task;
+ if (mInTaskFragment != null) {
+ // mInTaskFragment is created and added to the leaf task by task fragment organizer's
+ // request. If the task was resolved and different than mInTaskFragment, reparent the
+ // task to mInTaskFragment for embedding.
+ if (mInTaskFragment.getTask() != task) {
+ task.reparent(mInTaskFragment, POSITION_TOP);
+ } else {
+ newParent = mInTaskFragment;
+ }
+ }
+ if (mStartActivity.getTaskFragment() == null
+ || mStartActivity.getTaskFragment() == newParent) {
+ newParent.addChild(mStartActivity, POSITION_TOP);
} else {
- mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
+ mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
}
}
@@ -2921,6 +2946,11 @@
return this;
}
+ ActivityStarter setInTaskFragment(TaskFragment taskFragment) {
+ mRequest.inTaskFragment = taskFragment;
+ return this;
+ }
+
ActivityStarter setWaitResult(WaitResult result) {
mRequest.waitResult = result;
return this;
@@ -3004,5 +3034,7 @@
pw.print(mDoResume);
pw.print(" mAddingToTask=");
pw.println(mAddingToTask);
+ pw.print(" mInTaskFragment=");
+ pw.println(mInTaskFragment);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 090a01a..b0d0f16 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1222,8 +1222,8 @@
// 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;
}
@@ -1881,25 +1881,42 @@
@Override
public void setFocusedTask(int taskId) {
enforceTaskPermission("setFocusedTask()");
- ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId);
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_ATTACHED_TASK_ONLY);
- if (task == null) {
- return;
- }
- final ActivityRecord r = task.topRunningActivityLocked();
- if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
+ setFocusedTask(taskId, null /* touchedActivity */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
+ void setFocusedTask(int taskId, ActivityRecord touchedActivity) {
+ ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d touchedActivity=%s", taskId,
+ touchedActivity);
+ final Task task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY);
+ if (task == null) {
+ return;
+ }
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (r == null) {
+ return;
+ }
+
+ if (r.moveFocusableActivityToTop("setFocusedTask")) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ } else if (touchedActivity != null && touchedActivity != r
+ && touchedActivity.getTask() == r.getTask()
+ && touchedActivity.getTaskFragment() != r.getTaskFragment()) {
+ // Set the focused app directly since the focused window is not on the
+ // top-most TaskFragment of the top-most Task
+ final DisplayContent displayContent = touchedActivity.getDisplayContent();
+ displayContent.setFocusedApp(touchedActivity);
+ mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /* updateInputWindows */);
+ }
+ }
+
@Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
@@ -3769,6 +3786,20 @@
}
}
+ @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) {
@@ -5055,6 +5086,34 @@
process.registerDisplayAreaConfigurationListener(imeContainer);
}
+ @Override
+ public void setRunningRemoteTransitionDelegate(IApplicationThread caller) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "setRunningRemoteTransition");
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ // Also only allow a process which is already runningRemoteAnimation to mark another
+ // process.
+ final WindowProcessController callingProc = getProcessController(callingPid,
+ callingUid);
+ if (callingProc == null || !callingProc.isRunningRemoteTransition()) {
+ final String msg = "Can't call setRunningRemoteTransition from a process (pid="
+ + callingPid + " uid=" + callingUid + ") which isn't itself running a "
+ + "remote transition.";
+ Slog.e(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final WindowProcessController wpc = getProcessController(caller);
+ if (wpc == null) {
+ Slog.w(TAG, "Unable to find process for application " + caller);
+ return;
+ }
+ wpc.setRunningRemoteAnimation(true /* running */);
+ callingProc.addRemoteAnimationDelegate(wpc);
+ }
+ }
+
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index d3d1c1c..74efe0c 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 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_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.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.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -348,6 +347,12 @@
*/
private int mVisibilityTransactionDepth;
+ /**
+ * Whether to the visibility updates that started from {@code RootWindowContainer} should be
+ * deferred.
+ */
+ private boolean mDeferRootVisibilityUpdate;
+
private ActivityMetricsLogger mActivityMetricsLogger;
/** Check if placing task or activity on specified display is allowed. */
@@ -1968,76 +1973,14 @@
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 +2007,7 @@
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 +2029,7 @@
}
// Update the current top activity.
- mTopResumedActivity = topRootTask.getResumedActivity();
+ mTopResumedActivity = topRootTask.getTopResumedActivity();
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
@@ -2326,6 +2269,14 @@
return mVisibilityTransactionDepth > 0;
}
+ void setDeferRootVisibilityUpdate(boolean deferUpdate) {
+ mDeferRootVisibilityUpdate = deferUpdate;
+ }
+
+ boolean isRootVisibilityUpdateDeferred() {
+ return mDeferRootVisibilityUpdate;
+ }
+
/**
* Called when the state or visibility of an attached activity is changed.
*
@@ -2393,8 +2344,7 @@
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 c1b287f..ac687dc 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_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.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.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 @@
/**
* 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 @@
}
/**
- * 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 @@
* 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 @@
+ "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 @@
+ "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 @@
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 @@
(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/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index faeb4ba..4355b38 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -166,6 +166,10 @@
setReady(id, true);
}
+ boolean isReady(int id) {
+ return mActiveSyncs.get(id).mReady;
+ }
+
/**
* Aborts the sync (ie. it doesn't wait for ready or anything to finish)
*/
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 8fbe177..2eeabf2 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.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 @@
* 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 @@
}
/** 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 @@
* 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 baa27e3..99f6fd4 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 @@
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 47d7c9d..3d7ac6c 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.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 @@
*/
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 a72d9aa..632662d 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_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.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 @@
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)
@@ -689,6 +696,8 @@
// well and thus won't change the top resumed / focused record
boolean mDontMoveToTop;
+ private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -763,6 +772,12 @@
mTmpWindow = null;
return true;
}
+
+ if (focusedApp.getTask() == activity.getTask()
+ && focusedApp.getTaskFragment() != activity.getTaskFragment()) {
+ // Do not use the activity window of another TaskFragment in the same leaf Task
+ return false;
+ }
}
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
@@ -1012,6 +1027,8 @@
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 +1926,6 @@
}
}
}
-
- if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onRotationChanged(this);
- }
}
void configureDisplayPolicy() {
@@ -2480,7 +2493,7 @@
@Override
boolean isVisibleRequested() {
- return isVisible();
+ return isVisible() && !mRemoved && !mRemoving;
}
@Override
@@ -3095,7 +3108,11 @@
screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
- mAppTransition.dumpDebug(proto, APP_TRANSITION);
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION);
+ } else {
+ mAppTransition.dumpDebug(proto, APP_TRANSITION);
+ }
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -4499,6 +4516,8 @@
}
}
+ // clear first just in case.
+ mTmpActivityList.clear();
// Time to remove any exiting applications?
forAllRootTasks(task -> {
final ArrayList<ActivityRecord> activities = task.mExitingActivities;
@@ -4506,16 +4525,24 @@
final ActivityRecord activity = activities.get(j);
if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
&& (!activity.mIsExiting || activity.isEmpty())) {
- // Make sure there is no animation running on this activity, so any windows
- // associated with it will be removed as soon as their animations are
- // complete.
- cancelAnimation();
- ProtoLog.v(WM_DEBUG_ADD_REMOVE,
- "performLayout: Activity exiting now removed %s", activity);
- activity.removeIfPossible();
+ mTmpActivityList.add(activity);
}
}
});
+ if (!mTmpActivityList.isEmpty()) {
+ // Make sure there is no animation running on this activity, so any windows
+ // associated with it will be removed as soon as their animations are
+ // complete.
+ cancelAnimation();
+ }
+ for (int i = 0; i < mTmpActivityList.size(); ++i) {
+ final ActivityRecord activity = mTmpActivityList.get(i);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "performLayout: Activity exiting now removed %s", activity);
+ activity.removeIfPossible();
+ }
+ // Clear afterwards so we don't hold references.
+ mTmpActivityList.clear();
}
@Override
@@ -5017,6 +5044,12 @@
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
+ // TODO(b/191375840): decouple "forwardness" from transition system.
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ @WindowManager.TransitionType int type =
+ mAtmService.getTransitionController().getCollectingTransitionType();
+ return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
+ }
return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
|| mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
}
@@ -5600,6 +5633,14 @@
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 +5881,21 @@
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 97c19ab..d5a4345 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_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_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;
@@ -173,6 +175,7 @@
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
import java.io.PrintWriter;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -313,17 +316,26 @@
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 String mFocusedApp;
private int mLastDisableFlags;
private int mLastAppearance;
private int mLastFullscreenAppearance;
private int mLastDockedAppearance;
private int mLastBehavior;
+ private final InsetsState mRequestedState = new InsetsState();
private final Rect mNonDockedRootTaskBounds = new Rect();
private final Rect mDockedRootTaskBounds = new Rect();
private final Rect mLastNonDockedRootTaskBounds = new Rect();
private final Rect mLastDockedRootTaskBounds = new Rect();
- // What we last reported to system UI about whether the focused window is fullscreen/immersive.
+ // What we last reported to input dispatcher about whether the focused window is fullscreen.
private boolean mLastFocusIsFullscreen = false;
// If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
@@ -333,6 +345,7 @@
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 +357,6 @@
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 +441,10 @@
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 +626,8 @@
}
};
displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
+ mService.mAtmService.getTransitionController().registerLegacyListener(
+ mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
@@ -1079,7 +1094,9 @@
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 +1106,22 @@
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 +1182,14 @@
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 +1280,17 @@
}
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 +1425,7 @@
/**
* @return true if the system bars are forced to stay visible
*/
- public boolean areSystemBarsForcedShownLw(WindowState windowState) {
+ public boolean areSystemBarsForcedShownLw() {
return mForceShowSystemBars;
}
@@ -1423,13 +1460,30 @@
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);
+ }
}
- contentFrames.put(win.mAttrs.type, contentFrame);
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ contentFrames.put(win.mAttrs.type, contentFrame);
+ }
mDisplayContent.getInsetsStateController().computeSimulatedState(
win, displayFrames, simulatedWindowFrames);
}
@@ -1440,15 +1494,31 @@
* 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 +1537,7 @@
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 +1588,18 @@
} 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 +1608,16 @@
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 +1639,16 @@
* @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 +1656,7 @@
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 +1667,13 @@
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 +1773,17 @@
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 +1802,9 @@
mTopFullscreenOpaqueOrDimmingWindowState = null;
mTopDockedOpaqueWindowState = null;
mTopDockedOpaqueOrDimmingWindowState = null;
+ mNavBarColorWindowCandidate = null;
+ mNavBarBackgroundWindow = null;
mForceStatusBar = false;
- mForcingShowNavBar = false;
- mForcingShowNavBarLayer = -1;
mAllowLockscreenWhenOn = false;
mShowingDream = false;
@@ -1728,11 +1824,6 @@
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 +1894,34 @@
}
}
- // 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 +2230,37 @@
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 +2287,7 @@
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 +2297,21 @@
}
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 +2328,17 @@
* @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 +2459,9 @@
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 +2487,17 @@
@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 +2655,6 @@
final WindowState win = winCandidate;
mSystemUiControllingWindow = win;
- mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
-
final boolean inSplitScreen =
mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
if (inSplitScreen) {
@@ -2505,15 +2672,12 @@
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);
@@ -2522,6 +2686,8 @@
&& mLastFullscreenAppearance == fullscreenAppearance
&& mLastDockedAppearance == dockedAppearance
&& mLastBehavior == behavior
+ && mRequestedState.equals(win.getRequestedState())
+ && Objects.equals(mFocusedApp, win.mAttrs.packageName)
&& mLastFocusIsFullscreen == isFullscreen
&& mLastNonDockedRootTaskBounds.equals(mNonDockedRootTaskBounds)
&& mLastDockedRootTaskBounds.equals(mDockedRootTaskBounds)) {
@@ -2537,6 +2703,8 @@
mLastFullscreenAppearance = fullscreenAppearance;
mLastDockedAppearance = dockedAppearance;
mLastBehavior = behavior;
+ mRequestedState.set(win.getRequestedState(), true /* copySources */);
+ mFocusedApp = win.mAttrs.packageName;
mLastFocusIsFullscreen = isFullscreen;
mLastNonDockedRootTaskBounds.set(mNonDockedRootTaskBounds);
mLastDockedRootTaskBounds.set(mDockedRootTaskBounds);
@@ -2555,7 +2723,7 @@
final int displayId = getDisplayId();
statusBar.setDisableFlags(displayId, disableFlags, cause);
statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- isNavbarColorManagedByIme, behavior, isFullscreen);
+ isNavbarColorManagedByIme, behavior, mRequestedState, mFocusedApp);
}
});
@@ -2572,8 +2740,7 @@
@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 +2749,59 @@
&& 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();
+ 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 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;
+ // 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 +2847,24 @@
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 +2920,35 @@
* @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 +3120,13 @@
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 +3208,17 @@
}
@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 316c20b..2f0d703 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 @@
/** 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 @@
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 @@
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));
+ && mTaskFragment.isTopActivityFocusable()
+ && (starting == null || !starting.isDescendantOf(mTaskFragment));
- mTask.forAllActivities(a -> {
- setActivityVisibilityState(a, starting, resumeTopActivity);
- });
- if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
- mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+ 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 @@
}
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 @@
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 @@
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 @@
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 da47328..4f6a693 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.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 @@
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 747d365..f3b9cdf 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_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 @@
}
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 f2f1926..6b126ca 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 @@
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 @@
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 @@
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 @@
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 @@
&& !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 @@
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 @@
&& (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 7daebff..cbd1314 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 @@
// 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/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3dbe79d..5a249a5 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -58,10 +58,12 @@
private final LetterboxSurface mLeft = new LetterboxSurface("left");
private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
private final LetterboxSurface mRight = new LetterboxSurface("right");
- // Prevents wallpaper from peeking through near rounded corners. It's not included in
- // mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains
- // or attachInput.
- private final LetterboxSurface mBehind = new LetterboxSurface("behind");
+ // One surface that fills the whole window is used over multiple surfaces to:
+ // - Prevents wallpaper from peeking through near rounded corners.
+ // - For "blurred wallpaper" background, to avoid having visible border between surfaces.
+ // One surface approach isn't always preferred over multiple surfaces due to rendering cost
+ // for overlaping an app window and letterbox surfaces.
+ private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
/**
@@ -104,7 +106,7 @@
mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin);
mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin);
- mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin);
+ mFullWindowSurface.layout(outer.left, outer.top, outer.right, outer.bottom, surfaceOrigin);
}
@@ -168,37 +170,46 @@
for (LetterboxSurface surface : mSurfaces) {
surface.remove();
}
- mBehind.remove();
+ mFullWindowSurface.remove();
}
/** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
public boolean needsApplySurfaceChanges() {
+ if (useFullWindowSurface()) {
+ return mFullWindowSurface.needsApplySurfaceChanges();
+ }
for (LetterboxSurface surface : mSurfaces) {
if (surface.needsApplySurfaceChanges()) {
return true;
}
}
- if (mAreCornersRounded.get() && mBehind.needsApplySurfaceChanges()) {
- return true;
- }
return false;
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- for (LetterboxSurface surface : mSurfaces) {
- surface.applySurfaceChanges(t);
- }
- if (mAreCornersRounded.get()) {
- mBehind.applySurfaceChanges(t);
+ if (useFullWindowSurface()) {
+ mFullWindowSurface.applySurfaceChanges(t);
+
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.remove();
+ }
} else {
- mBehind.remove();
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.applySurfaceChanges(t);
+ }
+
+ mFullWindowSurface.remove();
}
}
/** Enables touches to slide into other neighboring surfaces. */
void attachInput(WindowState win) {
- for (LetterboxSurface surface : mSurfaces) {
- surface.attachInput(win);
+ if (useFullWindowSurface()) {
+ mFullWindowSurface.attachInput(win);
+ } else {
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.attachInput(win);
+ }
}
}
@@ -208,6 +219,16 @@
surface.mInputInterceptor.mWindowHandle.displayId = displayId;
}
}
+ if (mFullWindowSurface.mInputInterceptor != null) {
+ mFullWindowSurface.mInputInterceptor.mWindowHandle.displayId = displayId;
+ }
+ }
+
+ /**
+ * Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
+ */
+ private boolean useFullWindowSurface() {
+ return mAreCornersRounded.get() || mHasWallpaperBackgroundSupplier.get();
}
private static class InputInterceptor {
@@ -308,6 +329,10 @@
mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
}
+ boolean isRemoved() {
+ return mSurface != null || mInputInterceptor != null;
+ }
+
public void remove() {
if (mSurface != null) {
mTransactionFactory.get().remove(mSurface).apply();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7174e68..eb7087c 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.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 @@
* 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 @@
}
/**
+ * 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 @@
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 @@
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 @@
}
/**
+ * 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 @@
}
/**
+ * 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 @@
* 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 a10b5d6..7cd9164 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.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 @@
// 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 9a6a518..0b80bae 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.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.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;
@@ -69,14 +74,9 @@
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;
@@ -816,8 +816,7 @@
}
// Initialize state of exiting tokens.
- final int numDisplays = mChildren.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.setExitingTokensHasVisible(false);
}
@@ -866,7 +865,7 @@
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
@@ -928,12 +927,12 @@
}
// Time to remove any exiting tokens?
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.removeExistingTokensIfPossible();
}
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
@@ -1864,7 +1863,7 @@
if (focusedRootTask == null) {
return null;
}
- final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity != null && resumedActivity.app != null) {
return resumedActivity;
}
@@ -1886,11 +1885,11 @@
// 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;
@@ -1916,7 +1915,8 @@
return;
}
- if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
+ if (rootTask.getVisibility(null /* starting */)
+ == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
return;
}
@@ -1973,7 +1973,8 @@
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- if (mTaskSupervisor.inActivityVisibilityUpdate()) {
+ if (mTaskSupervisor.inActivityVisibilityUpdate()
+ || mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
// Don't do recursive work.
return;
}
@@ -2185,7 +2186,7 @@
// 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
@@ -2385,7 +2386,9 @@
if (displayShouldSleep) {
rootTask.goToSleepIfPossible(false /* shuttingDown */);
} else {
- rootTask.awakeFromSleepingLocked();
+ rootTask.forAllLeafTasksAndLeafTaskFragments(
+ taskFragment -> taskFragment.awakeFromSleeping(),
+ true /* traverseTopToBottom */);
if (rootTask.isFocusedRootTaskOnDisplay()
&& !mTaskSupervisor.getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2747,8 +2750,8 @@
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);
}
@@ -3335,7 +3338,7 @@
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);
@@ -3350,7 +3353,7 @@
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;
@@ -3368,7 +3371,7 @@
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());
@@ -3543,6 +3546,8 @@
}
}
+ // 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/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 4892005..2d4aef6 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -78,6 +78,18 @@
}
/**
+ * Constructs a new instance from a bundle and provided pid/uid.
+ *
+ * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
+ */
+ static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) {
+ return bOptions != null
+ ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions),
+ callingPid, callingUid)
+ : null;
+ }
+
+ /**
* Constructs a new instance and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
* this object.
@@ -91,6 +103,17 @@
}
/**
+ * Constructs a new instance.
+ *
+ * @param options The options to wrap.
+ */
+ private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
+ mOriginalCallingPid = callingPid;
+ mOriginalCallingUid = callingUid;
+ mOriginalOptions = options;
+ }
+
+ /**
* Overrides options with options from a caller and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
* method.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 777306a..c803f06 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_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_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_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.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_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.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_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.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.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.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 @@
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 @@
// code.
static final int PERSIST_TASK_VERSION = 1;
- static final int INVALID_MIN_SIZE = -1;
private float mShadowRadius = 0;
/**
@@ -335,36 +300,6 @@
// 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 @@
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 @@
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 @@
/** 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 @@
/** 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 @@
}
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 @@
*/
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 @@
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
boolean _removeWithTaskOrganizer) {
- super(atmService.mWindowManager);
+ super(atmService, null /* fragmentToken */, _createdByOrganizer);
- mAtmService = atmService;
- mTaskSupervisor = atmService.mTaskSupervisor;
- mRootWindowContainer = mAtmService.mRootWindowContainer;
mTaskId = _taskId;
mUserId = _userId;
mResizeMode = resizeMode;
@@ -875,7 +643,6 @@
: new PersistedTaskSnapshotData();
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
- mRemoteToken = new RemoteToken(this);
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -913,7 +680,6 @@
mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
- mCreatedByOrganizer = _createdByOrganizer;
mLaunchCookie = _launchCookie;
mDeferTaskAppear = _deferTaskAppear;
mRemoveWithTaskOrganizer = _removeWithTaskOrganizer;
@@ -1466,64 +1232,80 @@
mRootWindowContainer.updateUIDsPresentOnDisplay();
}
- void cleanUpActivityReferences(ActivityRecord r) {
- // mPausingActivity is set at leaf task
- if (mPausingActivity != null && mPausingActivity == r) {
- mPausingActivity = null;
+ /** 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;
+ }
+ }
}
- if (mResumedActivity != null && mResumedActivity == r) {
- setResumedActivity(null, "cleanUpActivityReferences");
+ 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 WindowContainer parent = getParent();
- if (parent != null && parent.asTask() != null) {
- parent.asTask().cleanUpActivityReferences(r);
- return;
- }
- r.removeTimeouts();
- mExitingActivities.remove(r);
- }
-
- /** @return the currently resumed activity. */
- ActivityRecord getResumedActivity() {
- if (isLeafTask()) {
- return mResumedActivity;
- }
-
- 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) {
@@ -1562,11 +1344,6 @@
mTaskId, mUserId);
}
- void setAdjacentTask(Task adjacent) {
- mAdjacentTask = adjacent;
- adjacent.mAdjacentTask = this;
- }
-
void setTaskToAffiliateWith(Task taskToAffiliateWith) {
closeRecentsChain();
mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -1675,22 +1452,7 @@
}
@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);
@@ -1706,10 +1468,17 @@
// 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) {
@@ -1736,10 +1505,6 @@
updateEffectiveIntent();
}
- void addChild(ActivityRecord r) {
- addChild(r, Integer.MAX_VALUE /* add on top */);
- }
-
@Override
void removeChild(WindowContainer child) {
removeChild(child, "removeChild");
@@ -1985,32 +1750,6 @@
&& 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.
*
@@ -2146,60 +1885,6 @@
}
}
- 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);
@@ -2208,32 +1893,6 @@
}
}
- /**
- * 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.
@@ -2526,400 +2185,6 @@
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;
@@ -2968,22 +2233,14 @@
}
}
- 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. */
@@ -3098,12 +2355,12 @@
// 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;
}
@@ -3152,17 +2409,16 @@
// 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;
}
}
@@ -3173,6 +2429,12 @@
} 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);
}
@@ -3554,18 +2816,6 @@
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
@@ -3576,45 +2826,6 @@
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) {
@@ -3708,19 +2919,6 @@
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);
@@ -3775,12 +2973,6 @@
});
}
- boolean isTopActivityFocusable() {
- final ActivityRecord r = topRunningActivity();
- return r != null ? r.isFocusable()
- : (isFocusable() && getWindowConfiguration().canReceiveKeys());
- }
-
boolean isFocusableAndVisible() {
return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
}
@@ -3896,6 +3088,41 @@
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;
@@ -4066,6 +3293,9 @@
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.
@@ -4124,6 +3354,7 @@
: INVALID_TASK_ID;
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
+ info.isSleeping = shouldSleepActivities();
ActivityRecord topRecord = getTopNonFinishingActivity();
info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
}
@@ -4191,184 +3422,6 @@
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;
@@ -4551,7 +3604,7 @@
// 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
@@ -5294,9 +4347,7 @@
return super.isAlwaysOnTop();
}
- /**
- * Returns whether this task is currently forced to be hidden for any reason.
- */
+ @Override
protected boolean isForceHidden() {
return mForceHiddenFlags != 0;
}
@@ -5449,8 +4500,10 @@
mAtmService.continueWindowLayout();
}
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- mRootWindowContainer.resumeFocusedTasksTopActivities();
+ if (!mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
}
void resumeNextFocusAfterReparent() {
@@ -5582,19 +4635,6 @@
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 */);
@@ -5613,302 +4653,13 @@
* 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() {
@@ -5931,10 +4682,10 @@
* 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,
@@ -5950,21 +4701,22 @@
* 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),
@@ -6039,25 +4791,6 @@
}
}
- /** @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.
*
@@ -6098,7 +4831,8 @@
if (!child.isTopActivityFocusable()) {
continue;
}
- if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
+ if (child.getVisibility(null /* starting */)
+ != TASK_FRAGMENT_VISIBILITY_VISIBLE) {
break;
}
@@ -6145,383 +4879,25 @@
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);
+ final boolean[] resumed = new boolean[1];
+ final TaskFragment topFragment = topActivity.getTaskFragment();
+ resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
+ forAllLeafTaskFragments(f -> {
+ if (topFragment == f) {
+ return;
}
- }
-
- // 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);
- }
- 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];
}
/**
@@ -6601,7 +4977,6 @@
// Slot the activity into the history root task and proceed
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
+ "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
- task.positionChildAtTop(r);
// The transition animation and starting window are not needed if {@code allowMoveToFront}
// is false, because the activity won't be visible.
@@ -7202,13 +5577,6 @@
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) {
@@ -7262,114 +5630,35 @@
}
}
- /**
- * 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;
-
- 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;
- }
-
- 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);
- }
-
- printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
-
- return printed;
+ return dump(" ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */);
}
- private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
- if (!hasChild()) {
- return false;
+ @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);
}
- 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;
+ if (dumpAll) {
+ printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+ prefix + " mLastPausedActivity: ", null);
+ }
+ 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 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) {
@@ -7754,7 +6043,8 @@
/** Returns true if a removal action is still being deferred. */
boolean handleCompleteDeferredRemoval() {
- if (isAnimating(TRANSITION | CHILDREN)) {
+ if (isAnimating(TRANSITION | CHILDREN)
+ || mAtmService.getTransitionController().inTransition(this)) {
return true;
}
@@ -7769,6 +6059,7 @@
return mAnimatingActivityRegistry;
}
+ @Override
void executeAppTransition(ActivityOptions options) {
mDisplayContent.executeAppTransition();
ActivityOptions.abort(options);
@@ -7791,10 +6082,6 @@
return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
}
- boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mAtmService.mShuttingDown;
- }
-
private Rect getRawBounds() {
return super.getBounds();
}
@@ -7813,14 +6100,12 @@
}
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());
@@ -7828,11 +6113,7 @@
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);
@@ -7849,6 +6130,8 @@
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 d450dbf..10c16ff 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.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 @@
// 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 @@
+ 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 @@
// 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 @@
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 @@
}
// 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 @@
// 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 @@
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 @@
*/
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 0000000..25d3033
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -0,0 +1,2117 @@
+/*
+ * 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.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;
+
+ /** Avoid reentrant of {@link #removeImmediately()}. */
+ private boolean mRemoving;
+
+ // 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. */
+ @Nullable
+ private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+
+ /** Client assigned unique token for this TaskFragment if this is created by an organizer. */
+ @Nullable
+ private IBinder mFragmentToken;
+
+ 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, IBinder fragmentToken,
+ boolean createdByOrganizer) {
+ super(atmService.mWindowManager);
+
+ mAtmService = atmService;
+ mTaskSupervisor = mAtmService.mTaskSupervisor;
+ mRootWindowContainer = mAtmService.mRootWindowContainer;
+ mCreatedByOrganizer = createdByOrganizer;
+ mTaskFragmentOrganizerController =
+ mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mFragmentToken = fragmentToken;
+ mRemoteToken = new RemoteToken(this);
+ }
+
+ void setAdjacentTaskFragment(TaskFragment taskFragment) {
+ mAdjacentTaskFragment = taskFragment;
+ taskFragment.mAdjacentTaskFragment = this;
+ }
+
+ void setTaskFragmentOrganizer(ITaskFragmentOrganizer organizer) {
+ mTaskFragmentOrganizer = organizer;
+ }
+
+ 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);
+ }
+ }
+
+ 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,
+ 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;
+ }
+
+ @Override
+ void removeImmediately() {
+ if (mRemoving) {
+ return;
+ }
+ mRemoving = true;
+ super.removeImmediately();
+ sendTaskFragmentVanished();
+ mRemoving = false;
+ }
+
+ 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 0000000..1423272
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -0,0 +1,234 @@
+/*
+ * 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.Slog;
+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.ArrayList;
+import java.util.Map;
+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 Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
+ new WeakHashMap<>();
+ private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
+ new WeakHashMap<>();
+ /**
+ * A Map which manages the relationship between
+ * {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
+ */
+ private final ArrayMap<IBinder, TaskFragmentController> mTaskFragmentOrganizerControllers =
+ new ArrayMap<>();
+
+ TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
+ mAtmService = atm;
+ mGlobalLock = atm.mGlobalLock;
+ }
+
+ /**
+ * A class to manage {@link ITaskFragmentOrganizer} and its organized
+ * {@link TaskFragment TaskFragments}.
+ */
+ private class TaskFragmentController implements IBinder.DeathRecipient {
+ private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
+ private final ITaskFragmentOrganizer mOrganizer;
+
+ TaskFragmentController(ITaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ try {
+ mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mGlobalLock) {
+ removeOrganizer(mOrganizer);
+ }
+ }
+
+ void addTaskFragment(TaskFragment taskFragment) {
+ if (!mOrganizedTaskFragments.contains(taskFragment)) {
+ mOrganizedTaskFragments.add(taskFragment);
+ }
+ }
+
+ void removeTaskFragment(TaskFragment taskFragment) {
+ mOrganizedTaskFragments.remove(taskFragment);
+ }
+
+ void dispose() {
+ mOrganizedTaskFragments.forEach(TaskFragment::removeImmediately);
+ mOrganizedTaskFragments.clear();
+ mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
+ }
+ }
+
+ @Override
+ public void registerOrganizer(ITaskFragmentOrganizer organizer) {
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Register task fragment organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ if (mTaskFragmentOrganizerControllers.containsKey(organizer.asBinder())) {
+ throw new IllegalStateException(
+ "Replacing existing organizer currently unsupported");
+ }
+ mTaskFragmentOrganizerControllers.put(organizer.asBinder(),
+ new TaskFragmentController(organizer));
+ }
+ }
+
+ @Override
+ public void unregisterOrganizer(ITaskFragmentOrganizer organizer) {
+ validateAndGetController(organizer);
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Unregister task fragment organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ removeOrganizer(organizer);
+ }
+ }
+
+ void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ final TaskFragmentController controller = validateAndGetController(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");
+ controller.addTaskFragment(tf);
+ try {
+ organizer.onTaskFragmentAppeared(
+ new TaskFragmentAppearedInfo(info, outSurfaceControl));
+ mLastSentTaskFragmentInfos.put(tf, info);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentAppeared callback", e);
+ }
+ }
+
+ void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ validateAndGetController(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) {
+ Slog.e(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
+ }
+ }
+
+ void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ final TaskFragmentController controller = validateAndGetController(organizer);
+
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
+ try {
+ organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentVanished callback", e);
+ }
+ mLastSentTaskFragmentInfos.remove(tf);
+ mLastSentTaskFragmentParentConfigs.remove(tf);
+ controller.removeTaskFragment(tf);
+ }
+
+ void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ validateAndGetController(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) {
+ Slog.e(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
+ }
+ }
+
+ private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+ final TaskFragmentController controller = validateAndGetController(organizer);
+ // remove all of the children of the organized TaskFragment
+ controller.dispose();
+ mTaskFragmentOrganizerControllers.remove(organizer.asBinder());
+ }
+
+ /**
+ * 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 TaskFragmentController validateAndGetController(ITaskFragmentOrganizer organizer) {
+ final TaskFragmentController controller =
+ mTaskFragmentOrganizerControllers.get(organizer.asBinder());
+ if (controller == null) {
+ throw new IllegalArgumentException(
+ "TaskFragmentOrganizer has not been registered. Organizer=" + organizer);
+ }
+ return controller;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 2dc63ce..a507abd 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0cd09807..0a8a937 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;
@@ -38,6 +40,9 @@
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -124,9 +129,16 @@
/** 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 +148,10 @@
mSyncId = mSyncEngine.startSyncSet(this);
}
+ void addFlag(int flag) {
+ mFlags |= flag;
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
@@ -207,6 +223,15 @@
}
/**
+ * 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 +300,28 @@
}
// 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 +330,14 @@
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 +346,7 @@
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 +377,7 @@
mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
.getPendingTransaction().merge(transaction);
mSyncId = -1;
+ mOverrideOptions = null;
return;
}
@@ -340,9 +391,15 @@
// 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);
+ reportStartReasonsToLogger();
+
// Manually show any activities that are visibleRequested. This is needed to properly
// support simultaneous animation queueing/merging. Specifically, if transition A makes
// an activity invisible, it's finishTransaction (which is applied *after* the animation)
@@ -360,6 +417,7 @@
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 +433,7 @@
cleanUpOnFailure();
}
mSyncId = -1;
+ mOverrideOptions = null;
}
/**
@@ -394,6 +453,100 @@
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 =
@@ -427,6 +580,23 @@
}
}
+ private void reportStartReasonsToLogger() {
+ // Record transition start in metrics logger. We just assume everything is "DRAWN"
+ // at this point since splash-screen is a presentation (shell) detail.
+ ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
+ if (r == null) continue;
+ // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
+ // ready due to starting-window.
+ reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
+ && !r.mLastAllReadyAtSync)
+ ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+ }
+ mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+ reasons);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -629,6 +799,8 @@
if (reportIfNotTop(wc)) {
tmpList.add(wc);
}
+ // Wallpaper must be the top (regardless of how nested it is in DisplayAreas).
+ boolean skipIntermediateReports = isWallpaper(wc);
for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) {
if (!p.isAttached() || !changes.get(p).hasChanged(p)) {
// Again, we're skipping no-ops
@@ -637,7 +809,9 @@
if (participants.contains(p)) {
topParent = p;
break;
- } else if (reportIfNotTop(p)) {
+ } else if (isWallpaper(p)) {
+ skipIntermediateReports = true;
+ } else if (reportIfNotTop(p) && !skipIntermediateReports) {
tmpList.add(p);
}
}
@@ -723,17 +897,11 @@
}
// Find the top-most shared ancestor of app targets
- WindowContainer ancestor = null;
- for (int i = appTargets.size() - 1; i >= 0; --i) {
- final WindowContainer wc = appTargets.valueAt(i);
- ancestor = wc;
- break;
- }
- if (ancestor == null) {
+ if (appTargets.isEmpty()) {
out.setRootLeash(new SurfaceControl(), 0, 0);
return out;
}
- ancestor = ancestor.getParent();
+ WindowContainer ancestor = appTargets.valueAt(appTargets.size() - 1).getParent();
// Go up ancestor parent chain until all targets are descendants.
ancestorLoop:
@@ -805,6 +973,10 @@
return out;
}
+ boolean getLegacyIsReady() {
+ return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
+ }
+
static Transition fromBinder(IBinder binder) {
return (Transition) binder;
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index cc63c49..3d7b34b 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -17,6 +17,13 @@
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_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import android.annotation.NonNull;
@@ -24,14 +31,19 @@
import android.app.ActivityManager;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
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;
@@ -41,15 +53,25 @@
class TransitionController {
private static final String TAG = "TransitionController";
+ // State constants to line-up with legacy app-transition proto expectations.
+ private static final int LEGACY_STATE_IDLE = 0;
+ private static final int LEGACY_STATE_READY = 1;
+ private static final int LEGACY_STATE_RUNNING = 2;
+
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 +79,18 @@
}
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) */
@@ -76,7 +103,7 @@
* Creates a transition. It can immediately collect participants.
*/
@NonNull
- Transition createTransition(@WindowManager.TransitionType int type,
+ private Transition createTransition(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags) {
if (mTransitionPlayer == null) {
throw new IllegalStateException("Shell Transitions not enabled");
@@ -87,6 +114,7 @@
mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
mCollectingTransition);
+ dispatchLegacyAppTransitionPending();
return mCollectingTransition;
}
@@ -154,6 +182,11 @@
return false;
}
+ @WindowManager.TransitionType
+ int getCollectingTransitionType() {
+ return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
+ }
+
/**
* @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition)
*/
@@ -240,6 +273,12 @@
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 +300,7 @@
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record);
mPlayingTransitions.remove(record);
record.finishTransition();
+ mRunningLock.doNotifyLocked();
}
void moveToPlaying(Transition transition) {
@@ -279,4 +319,109 @@
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 */);
+ }
+ }
+
+ void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ int state = LEGACY_STATE_IDLE;
+ if (!mPlayingTransitions.isEmpty()) {
+ state = LEGACY_STATE_RUNNING;
+ } else if (mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) {
+ state = LEGACY_STATE_READY;
+ }
+ proto.write(AppTransitionProto.APP_TRANSITION_STATE, state);
+ proto.end(token);
+ }
+
+ 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 b1c7e19..913c5e5 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 @@
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 @@
}
}
+ /**
+ * 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 @@
}
}
+ 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 @@
}
/** Cheap way of doing cast and instanceof. */
+ TaskFragment asTaskFragment() {
+ return null;
+ }
+
+ /** Cheap way of doing cast and instanceof. */
WindowToken asWindowToken() {
return null;
}
@@ -3282,6 +3331,20 @@
}
/**
+ * Special helper to check that all windows are synced (vs just top one). This is only
+ * used to differentiate between starting-window vs full-drawn in activity-metrics reporting.
+ */
+ boolean allSyncFinished() {
+ if (!isVisibleRequested()) return true;
+ if (mSyncState != SYNC_STATE_READY) return false;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (!child.allSyncFinished()) return false;
+ }
+ return true;
+ }
+
+ /**
* Called during reparent to handle sync state when the hierarchy changes.
* If this is in a sync group and gets reparented out, it will cancel syncing.
* If this is not in a sync group and gets parented into one, it will prepare itself.
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index ffd6d21..baea854 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 @@
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 47087cf..5bc4d49 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.policy.WindowManagerPolicy;
import java.util.List;
+import java.util.Set;
/**
* Window manager local system service interface.
@@ -54,17 +55,18 @@
*/
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 @@
*
* @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 @@
* @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 @@
*/
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 @@
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 1ec9187..53e02e5 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.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 @@
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 @@
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 @@
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);
@@ -2717,8 +2720,8 @@
}
@Override
- public boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
- Bundle options) {
+ public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
+ type, int displayId, Bundle options) {
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2729,15 +2732,17 @@
if (dc == null) {
ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach"
+ " to a non-existing display:%d", displayId);
- return false;
+ return null;
}
// TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
// the feature b/155340867 is completed.
final DisplayArea da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */);
+ // TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done
+ // in return value of attachWindowContextToDisplayArea.
mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
callingUid, type, options);
- return true;
+ return da.getConfiguration();
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -5390,6 +5395,25 @@
}
}
+ 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 +7595,7 @@
public void registerAppTransitionListener(AppTransitionListener listener) {
synchronized (mGlobalLock) {
getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener);
+ mAtmService.getTransitionController().registerLegacyListener(listener);
}
}
@@ -8160,11 +8185,11 @@
displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
true /* includingParents */);
}
- handleTaskFocusChange(touchedWindow.getTask());
+ handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
}
@VisibleForTesting
- void handleTaskFocusChange(Task task) {
+ void handleTaskFocusChange(Task task, ActivityRecord touchedActivity) {
if (task == null) {
return;
}
@@ -8183,7 +8208,7 @@
}
}
- mAtmService.setFocusedTask(task.mTaskId);
+ mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
}
/**
@@ -8556,7 +8581,7 @@
}
if (win.mActivityRecord == null || !win.mActivityRecord.isState(
- Task.ActivityState.RESUMED)) {
+ ActivityRecord.State.RESUMED)) {
mDisplayHashController.sendDisplayHashError(callback,
DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
@@ -8611,4 +8636,23 @@
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 a94fd07..d5965494 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 @@
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.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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
}
+ 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 a82a478..eeb83c6 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -16,13 +16,19 @@
package com.android.server.wm;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
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 +40,7 @@
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;
@@ -42,14 +49,17 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
+import android.util.ArrayMap;
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,14 +105,21 @@
final TaskOrganizerController mTaskOrganizerController;
final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
+ final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
final TransitionController mTransitionController;
+ /**
+ * A Map which manages the relationship between
+ * {@link TaskFragmentCreationParams.mFragmentToken fragmentToken} and {@link TaskFragment}
+ */
+ private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
+ mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
mTransitionController = new TransitionController(atm);
}
@@ -126,10 +143,11 @@
if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
}
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- applyTransaction(t, -1 /*syncId*/, null /*transition*/);
+ applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -143,6 +161,7 @@
if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
}
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -162,7 +181,7 @@
if (callback != null) {
syncId = startSyncWithOrganizer(callback);
}
- applyTransaction(t, syncId, null /*transition*/);
+ applyTransaction(t, syncId, null /*transition*/, caller);
if (syncId >= 0) {
setSyncReady(syncId);
}
@@ -177,6 +196,7 @@
public IBinder startTransition(int type, @Nullable IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
enforceTaskPermission("startTransition()");
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -196,7 +216,7 @@
throw new IllegalArgumentException("Can't use legacy transitions in"
+ " compatibility mode with no WCT.");
}
- applyTransaction(t, -1 /* syncId */, null);
+ applyTransaction(t, -1 /* syncId */, null, caller);
return null;
}
transition = mTransitionController.createTransition(type);
@@ -205,7 +225,7 @@
if (t == null) {
t = new WindowContainerTransaction();
}
- applyTransaction(t, -1 /*syncId*/, transition);
+ applyTransaction(t, -1 /*syncId*/, transition, caller);
if (needsSetReady) {
transition.setReady();
}
@@ -221,6 +241,7 @@
@Nullable WindowContainerTransaction t,
@Nullable IWindowContainerTransactionCallback callback) {
enforceTaskPermission("finishTransition()");
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -231,7 +252,7 @@
// apply the incoming transaction before finish in case it alters the visibility
// of the participants.
if (t != null) {
- applyTransaction(t, syncId, null /*transition*/);
+ applyTransaction(t, syncId, null /*transition*/, caller);
}
getTransitionController().finishTransition(transitionToken);
if (syncId >= 0) {
@@ -247,12 +268,14 @@
/**
* @param syncId If non-null, this will be a sync-transaction.
* @param transition A transition to collect changes into.
+ * @param caller Info about the calling process.
*/
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
- @Nullable Transition transition) {
+ @Nullable Transition transition, @Nullable CallerInfo caller) {
int effects = 0;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
if (transition != null) {
// First check if we have a display rotation transition and if so, update it.
@@ -303,7 +326,7 @@
final boolean isInLockTaskMode = mService.isInLockTaskMode();
for (int i = 0; i < hopSize; ++i) {
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
- isInLockTaskMode);
+ isInLockTaskMode, caller);
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -341,6 +364,7 @@
task.setMainWindowSizeChangeTransaction(sft);
}
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
// Already calls ensureActivityConfig
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -362,6 +386,7 @@
mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
}
} finally {
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.continueWindowLayout();
}
}
@@ -458,7 +483,8 @@
}
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
- int syncId, @Nullable Transition transition, boolean isInLockTaskMode) {
+ int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
+ @Nullable CallerInfo caller) {
final int type = hop.getType();
switch (type) {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
@@ -480,7 +506,7 @@
} 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 +527,15 @@
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;
@@ -537,11 +565,68 @@
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
+ mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "launchTask HierarchyOp");
final Bundle launchOpts = hop.getLaunchOptions();
final int taskId = launchOpts.getInt(
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- mService.startActivityFromRecents(taskId, launchOpts);
+ final SafeActivityOptions safeOptions = caller != null
+ ? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid)
+ : SafeActivityOptions.fromBundle(launchOpts);
+ mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
+ taskId, safeOptions);
+ break;
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ final TaskFragmentCreationParams taskFragmentCreationOptions =
+ hop.getTaskFragmentCreationOptions();
+ createTaskFragment(taskFragmentCreationOptions);
+ 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 unknown or 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.");
+ }
+ deleteTaskFragment(taskFragment);
+ break;
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+ fragmentToken = hop.getContainer();
+ if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
+ throw new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token");
+ }
+ final Intent activityIntent = hop.getActivityIntent();
+ final Bundle activityOptions = hop.getLaunchOptions();
+ mService.getActivityStartController()
+ .startActivityInTaskFragment(mLaunchTaskFragments.get(fragmentToken),
+ activityIntent, activityOptions);
+ // TODO(b/189385246) : report the failure back to the organizer if the activity
+ // start failed
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+ fragmentToken = hop.getNewParent();
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
+ if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
+ throw new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token or activity.");
+ }
+ activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
+ final WindowContainer newParent = WindowContainer.fromBinder(hop.getNewParent());
+ if (oldParent == null || !oldParent.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + oldParent);
+ break;
+ }
+ reparentTaskFragment(oldParent, newParent);
break;
}
return effects;
@@ -704,13 +789,14 @@
}
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 +833,11 @@
return mDisplayAreaOrganizerController;
}
+ @Override
+ public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() {
+ return mTaskFragmentOrganizerController;
+ }
+
@VisibleForTesting
int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
@@ -795,7 +886,80 @@
}
}
+ /** 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);
}
+
+ void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams) {
+ final ActivityRecord ownerActivity =
+ ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
+ if (ownerActivity == null || ownerActivity.getTask() == null) {
+ // TODO(b/189385246) : report the failure back to the organizer
+ return;
+ }
+ // The ownerActivity has to belong to the same app as the root Activity of the target Task.
+ final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
+ if (rootActivity.getUid() != ownerActivity.getUid()) {
+ // TODO(b/189385246) : report the failure back to the organizer
+ return;
+ }
+ final TaskFragment taskFragment = new TaskFragment(mService,
+ creationParams.getFragmentToken(), true /* createdByOrganizer */);
+ ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
+ taskFragment.setWindowingMode(creationParams.getWindowingMode());
+ taskFragment.setBounds(creationParams.getInitialBounds());
+ taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer());
+ mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
+ }
+
+ void reparentTaskFragment(@NonNull WindowContainer oldParent,
+ @Nullable WindowContainer newParent) {
+ WindowContainer parent = newParent;
+ if (parent == null && oldParent.asTaskFragment() != null) {
+ parent = oldParent.asTaskFragment().getTask();
+ }
+ if (parent == null) {
+ // TODO(b/189385246) : report the failure back to the organizer
+ return;
+ }
+ while (oldParent.hasChild()) {
+ oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
+ }
+ }
+
+ void deleteTaskFragment(@NonNull TaskFragment taskFragment) {
+ final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
+ if (index < 0) {
+ throw new IllegalArgumentException(
+ "Not allowed to operate with invalid taskFragment");
+ }
+ mLaunchTaskFragments.removeAt(index);
+ taskFragment.removeImmediately();
+ }
+
+ static class CallerInfo {
+ final int mPid;
+ final int mUid;
+
+ CallerInfo() {
+ mPid = Binder.getCallingPid();
+ mUid = Binder.getCallingUid();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1364c72..65b065a 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 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_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;
@@ -75,6 +75,7 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -219,6 +220,10 @@
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
+ /** List of "chained" processes that are running remote animations for this process */
+ private final ArrayList<WeakReference<WindowProcessController>> mRemoteAnimationDelegates =
+ new ArrayList<>();
+
// The bits used for mActivityStateFlags.
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
@@ -735,10 +740,10 @@
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");
}
}
@@ -991,7 +996,7 @@
// 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;
@@ -1215,12 +1220,12 @@
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();
}
@@ -1596,11 +1601,38 @@
updateRunningRemoteOrRecentsAnimation();
}
+ /**
+ * Marks another process as a "delegate" animator. This means that process is doing some part
+ * of a remote animation on behalf of this process.
+ */
+ void addRemoteAnimationDelegate(WindowProcessController delegate) {
+ if (!isRunningRemoteTransition()) {
+ throw new IllegalStateException("Can't add a delegate to a process which isn't itself"
+ + " running a remote animation");
+ }
+ mRemoteAnimationDelegates.add(new WeakReference<>(delegate));
+ }
+
void updateRunningRemoteOrRecentsAnimation() {
+ if (!isRunningRemoteTransition()) {
+ // Clean-up any delegates
+ for (int i = 0; i < mRemoteAnimationDelegates.size(); ++i) {
+ final WindowProcessController delegate = mRemoteAnimationDelegates.get(i).get();
+ if (delegate == null) continue;
+ delegate.setRunningRemoteAnimation(false);
+ delegate.setRunningRecentsAnimation(false);
+ }
+ mRemoteAnimationDelegates.clear();
+ }
+
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::setRunningRemoteAnimation, mListener,
- mRunningRecentsAnimation || mRunningRemoteAnimation));
+ isRunningRemoteTransition()));
+ }
+
+ boolean isRunningRemoteTransition() {
+ return mRunningRecentsAnimation || mRunningRemoteAnimation;
}
/** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c3fc995..f829b0f 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_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.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.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;
@@ -874,6 +875,15 @@
}
/**
+ * Returns all the requested visibilities.
+ *
+ * @return an {@link InsetsState} as the requested visibilities.
+ */
+ InsetsState getRequestedState() {
+ return mRequestedInsetsState;
+ }
+
+ /**
* @see #getRequestedVisibility(int)
*/
void updateRequestedVisibility(InsetsState state) {
@@ -1259,8 +1269,8 @@
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 +1281,7 @@
/**
* 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 +1394,8 @@
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 +1488,18 @@
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;
@@ -1715,6 +1738,10 @@
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
+ @Nullable TaskFragment getTaskFragment() {
+ return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null;
+ }
+
@Nullable Task getRootTask() {
final Task task = getTask();
if (task != null) {
@@ -1842,9 +1869,8 @@
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 +1878,18 @@
}
@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 +1936,16 @@
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.
@@ -2878,9 +2921,14 @@
// means we need to intercept touches outside of that window. The dim layer
// user associated with the window (task or root task) will give us the good
// bounds, as they would be used to display the dim layer.
- final Task task = getTask();
- if (task != null) {
- task.getDimBounds(mTmpRect);
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
+ final Task task = taskFragment.asTask();
+ if (task != null) {
+ task.getDimBounds(mTmpRect);
+ } else {
+ mTmpRect.set(taskFragment.getBounds());
+ }
} else if (getRootTask() != null) {
getRootTask().getDimBounds(mTmpRect);
}
@@ -3863,7 +3911,7 @@
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 +4445,13 @@
}
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 +4461,54 @@
// 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 +4535,12 @@
}
// 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
@@ -5949,6 +6002,13 @@
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
+ if (mActivityRecord != null
+ && mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && mAttrs.type == TYPE_APPLICATION_STARTING) {
+ mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
+ .notifyStartingWindowDrawn(mActivityRecord);
+ }
+
if (postDrawTransaction != null) {
mSyncTransaction.merge(postDrawTransaction);
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fbfa400..fa32be3 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;
@@ -229,6 +231,11 @@
ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT,
"removeAllWindowsIfPossible: removing win=%s", win);
win.removeIfPossible();
+ if (i > mChildren.size()) {
+ // It's possible for removeIfPossible to delete siblings (for example if it is a
+ // starting window, it will perform operations on the ActivityRecord).
+ i = mChildren.size();
+ }
}
}
@@ -451,9 +458,24 @@
}
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index aca7cc9..5086060 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -378,6 +378,8 @@
"com.android.server.connectivity.IpConnectivityMetrics";
private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
"com.android.server.media.MediaCommunicationService";
+ private static final String APP_COMPAT_OVERRIDES_SERVICE_CLASS =
+ "com.android.server.compat.overrides.AppCompatOverridesService$Lifecycle";
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
private static final String GAME_MANAGER_SERVICE_CLASS =
@@ -2655,6 +2657,10 @@
mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("AppCompatOverridesService");
+ mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
+ t.traceEnd();
+
ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
START_BLOB_STORE_SERVICE);
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
new file mode 100644
index 0000000..bf97042
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
@@ -0,0 +1,302 @@
+/*
+ * 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.compat.overrides;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test class for {@link AppCompatOverridesParser}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesParserTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesParserTest {
+ private static final String PACKAGE_1 = "com.android.test1";
+ private static final String PACKAGE_2 = "com.android.test2";
+ private static final String PACKAGE_3 = "com.android.test3";
+ private static final String PACKAGE_4 = "com.android.test4";
+
+ private AppCompatOverridesParser mParser;
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mParser = new AppCompatOverridesParser(mPackageManager);
+ }
+
+ @Test
+ public void parseRemoveOverrides_emptyConfig_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+
+ assertThat(mParser.parseRemoveOverrides("", ownedChangeIds)).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasWildcardNoOwnedChangeIds_returnsEmpty() {
+ when(mPackageManager.getInstalledApplications(anyInt()))
+ .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+ assertThat(mParser.parseRemoveOverrides("*", /* ownedChangeIds= */ emptySet())).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasWildcard_returnsAllInstalledPackagesToAllOwnedIds() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+ when(mPackageManager.getInstalledApplications(anyInt()))
+ .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2),
+ createAppInfo(PACKAGE_3)));
+
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides("*", ownedChangeIds);
+
+ assertThat(result).hasSize(3);
+ assertThat(result.get(PACKAGE_1)).containsExactly(123L, 456L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(123L, 456L);
+ assertThat(result.get(PACKAGE_3)).containsExactly(123L, 456L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidWildcardSymbol_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+ when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
+ Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+ assertThat(mParser.parseRemoveOverrides("**", ownedChangeIds)).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasSingleEntry_returnsPackageToChangeIds() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12:34", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L, 34L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasMultipleEntries_returnsPackagesToChangeIds() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L, 56L, 78L));
+
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + "=*," + PACKAGE_3 + "=12:56:78," + PACKAGE_4
+ + "=", ownedChangeIds);
+
+ assertThat(result).hasSize(3);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L, 34L, 56L, 78L);
+ assertThat(result.get(PACKAGE_3)).containsExactly(12L, 56L, 78L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasPackageWithWildcardNoOwnedId_returnsWithoutPackage() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=*," + PACKAGE_2 + "=12", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidKeyValueListFormat_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L));
+
+ assertThat(mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + ">34", ownedChangeIds)).isEmpty();
+ }
+
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + "=12:56L:78," + PACKAGE_3
+ + "=34L", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(2);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L, 78L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_emptyConfig_returnsEmpty() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("")).isEmpty();
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasSingleChangeId_returnsChangeId() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("123")).containsExactly(123L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasMultipleChangeIds_returnsChangeIds() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,34,56")).containsExactly(12L,
+ 34L, 56L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+ // We add a valid entry before and after the invalid ones to make sure they are applied.
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,C34,56")).containsExactly(12L,
+ 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_emptyConfig_returnsEmpty() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).isEmpty();
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithSingleOverride_returnsOverride() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverridesToAdd_returnsOverrides() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,56::2:true,"
+ + "56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,1112::5:true,"
+ + "56:6::true,1112:6:7:false", /* versionCode= */
+ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(6);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(false).build());
+ assertThat(result.overridesToAdd.get(34L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled(
+ true).build());
+ assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled(
+ false).build());
+ assertThat(result.overridesToAdd.get(78L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled(
+ true).build());
+ assertThat(result.overridesToAdd.get(910L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build());
+ assertThat(result.overridesToAdd.get(1112L)).isEqualTo(
+ new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build());
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverridesToRemove_returnsOverrides() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::,34:1:2:", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToRemove).containsExactly(12L, 34L);
+ assertThat(result.overridesToAdd).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithBothOverridesToAddAndRemove_returnsOverrides() {
+ // Note that change 56 is both added and removed, therefore it will only be removed.
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "56:::,12:::true,34:::,56:3:7:true", /* versionCode= */ 5, /* changeIdsToSkip= */
+ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToRemove).containsExactly(34L, 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() {
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+ changeIdsToSkip.add(34L);
+ changeIdsToSkip.add(56L);
+ changeIdsToSkip.add(910L);
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::true,34:::,56:3:7:true,78:::", /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToRemove).containsExactly(78L);
+ }
+
+ @Test
+ public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() {
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+ changeIdsToSkip.add(12L);
+ changeIdsToSkip.add(34L);
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::true,34:::", /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result.overridesToAdd).isEmpty();
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() {
+ // We add a valid entry before and after the invalid ones to make sure they are applied.
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
+ + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:one:ten:",
+ /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(2);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(1).setMaxVersionCode(2).setEnabled(
+ false).build());
+ assertThat(result.overridesToRemove).containsExactly(34L);
+ }
+
+ private static ApplicationInfo createAppInfo(String packageName) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ return appInfo;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
new file mode 100644
index 0000000..d3f04f3
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -0,0 +1,380 @@
+/*
+ * 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.compat.overrides;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.app.compat.PackageOverride;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Test class for {@link AppCompatOverridesService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesServiceTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesServiceTest {
+ private static final String NAMESPACE_1 = "namespace_1";
+ private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(NAMESPACE_1);
+
+ private static final String PACKAGE_1 = "com.android.test1";
+ private static final String PACKAGE_2 = "com.android.test2";
+ private static final String PACKAGE_3 = "com.android.test3";
+ private static final String PACKAGE_4 = "com.android.test4";
+ private static final String PACKAGE_5 = "com.android.test5";
+
+ private MockContext mMockContext;
+ private AppCompatOverridesService mService;
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private IPlatformCompat mPlatformCompat;
+
+ @Captor
+ private ArgumentCaptor<CompatibilityOverrideConfig> mOverridesToAddConfigCaptor;
+ @Captor
+ private ArgumentCaptor<CompatibilityOverridesToRemoveConfig> mOverridesToRemoveConfigCaptor;
+
+ @Rule
+ public TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfigRule();
+
+ class MockContext extends ContextWrapper {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public Executor getMainExecutor() {
+ // Run on current thread
+ return Runnable::run;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = new MockContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ mService = new AppCompatOverridesService(mMockContext, mPlatformCompat,
+ SUPPORTED_NAMESPACES);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagNotSet_appliesPackageOverrides()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 3);
+ mockGetApplicationInfoNotInstalled(PACKAGE_2);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 10);
+ mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 1);
+ mockGetApplicationInfo(PACKAGE_5, /* versionCode= */ 1);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true,456:::")
+ .setString(PACKAGE_4, "")
+ .setString(PACKAGE_5, "123:::,789:::")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789").build());
+
+ Map<Long, PackageOverride> addedOverrides;
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+ assertThat(addedOverrides).hasSize(2);
+ assertThat(addedOverrides.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(addedOverrides.get(456L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+ addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+ assertThat(addedOverrides).hasSize(1);
+ assertThat(addedOverrides.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(10).setMaxVersionCode(
+ 11).setEnabled(false).build());
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ // Package 4
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ // Package 5
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_5));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_5));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagSetBefore_skipsOverridesToRemove()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456," + PACKAGE_2 + "=123")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_4, "123:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "456:::true").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(789L);
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ // Package 4 (not applied because it hasn't changed after the listener was added)
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagChangedNoPackageOverridesFlags_removesOnly()
+ throws Exception {
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES,
+ PACKAGE_1 + "=123:456," + PACKAGE_2 + "=789").build());
+
+ // Package 1
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L);
+ // Package 2
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagAndSomePackageOverrideFlagsChanged_ok()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456")
+ .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_3, "456:::false,789:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_2 + "=123," + PACKAGE_3 + "=789")
+ .setString(PACKAGE_2, "123:::true,456:::").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L,
+ 789L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.size()).isAtLeast(2);
+ assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L);
+ assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L);
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_ownedChangeIdsFlagAndSomePackageOverrideFlagsChanged_ok()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=*")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_3, "456:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
+ .setString(PACKAGE_2, "123:::true").build());
+
+ // Package 1
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L,
+ 789L);
+ // Package 2
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L);
+ // Package 3
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_3));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ }
+
+ @Test
+ public void onPropertiesChanged_platformCompatThrowsExceptionForSomeCalls_skipsFailedCalls()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 0);
+ doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ doThrow(new RemoteException()).when(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456:::")
+ .setString(PACKAGE_2, "123:::true,456:::")
+ .setString(PACKAGE_3, "123:::true,456:::")
+ .setString(PACKAGE_4, "123:::true,456:::").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ // Package 2
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_2));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ // Package 4
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ }
+
+ private void mockGetApplicationInfo(String packageName, long versionCode)
+ throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())).thenReturn(
+ createAppInfo(versionCode));
+ }
+
+ private void mockGetApplicationInfoNotInstalled(String packageName) throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+ .thenThrow(new PackageManager.NameNotFoundException());
+ }
+
+ private static ApplicationInfo createAppInfo(long versionCode) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.longVersionCode = versionCode;
+ return appInfo;
+ }
+}
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 64b24c1..43188f6 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 @@
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 @@
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 0e40669..d68b814 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 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 @@
}
@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 @@
}
@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 @@
}
}
+ @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 d6c11a5..e612d12 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.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/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index 80e81d6..554f0a4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -29,6 +29,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -176,7 +177,7 @@
}
@Test
- public void testEventHandler_shouldChangeAfterOnDisplayChanged() {
+ public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() {
prepareLooper();
// Check if there is only one mEventHandler when there is one default display.
@@ -184,13 +185,51 @@
assertEquals(1, mEventHandler.size());
// Check if it has correct numbers of mEventHandler for corresponding displays.
- setDisplayCount(4);
- mA11yInputFilter.onDisplayChanged();
- assertEquals(4, mEventHandler.size());
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+ assertEquals(2, mEventHandler.size());
+
+ EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY);
+ assertNotNull(next);
+
+ // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
+ // DEFAULT_DISPLAY.
+ for (int i = 1; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ next = next.getNext();
+ }
+ }
+
+ @Test
+ public void testEventHandler_shouldDecreaseAfterOnDisplayRemoved() {
+ prepareLooper();
setDisplayCount(2);
- mA11yInputFilter.onDisplayChanged();
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
assertEquals(2, mEventHandler.size());
+
+ // Check if it has correct numbers of mEventHandler for corresponding displays.
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ assertEquals(1, mEventHandler.size());
+
+ EventStreamTransformation eventHandler = mEventHandler.get(SECOND_DISPLAY);
+ assertNull(eventHandler);
+ }
+
+ @Test
+ public void testEventHandler_shouldNoChangedInOtherDisplayAfterOnDisplayRemoved() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ EventStreamTransformation eventHandlerBeforeDisplayRemoved =
+ mEventHandler.get(DEFAULT_DISPLAY);
+
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ EventStreamTransformation eventHandlerAfterDisplayRemoved =
+ mEventHandler.get(DEFAULT_DISPLAY);
+
+ assertEquals(eventHandlerBeforeDisplayRemoved, eventHandlerAfterDisplayRemoved);
}
@Test
@@ -240,7 +279,7 @@
}
@Test
- public void testInputEvent_shouldClearEventsForAllEventHandlers() {
+ public void testInputEvent_shouldClearEventsForDisplayEventHandlers() {
prepareLooper();
mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
@@ -253,13 +292,71 @@
send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
assertEquals(2, mCaptor1.mEvents.size());
- // InputEvent with different input source should trigger clearEvents() for each
- // EventStreamTransformation in EventHandler.
+ // InputEvent with different input source to the same display should trigger
+ // clearEvents() for the EventHandler in this display.
send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_MOUSE));
assertEquals(1, mCaptor1.mEvents.size());
}
@Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayEventHandlers() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(2, mEventHandler.size());
+
+ mCaptor1 = new EventCaptor();
+ mCaptor2 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+ mEventHandler.put(SECOND_DISPLAY, mCaptor2);
+
+ // InputEvent with different displayId should be dispatched to corresponding EventHandler.
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+
+ // InputEvent with different input source should not trigger clearEvents() for
+ // the EventHandler in the other display.
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_MOUSE));
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayAdded() {
+ prepareLooper();
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayRemoved() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationGestureHandler() {
prepareLooper();
doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW).when(
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 00daa5c..432a500 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.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 e4d51e4..4afe099 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 @@
@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 @@
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 @@
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 78e651b..c62cae5 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 @@
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 d4908ee..59b69f9 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 @@
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 @@
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 1603087..4ce9ba0 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.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 7bf0bb8..8a745d5 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 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.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 @@
@Mock
private AccessibilityManagerService mMockAms;
+ @Mock
+ private AccessibilityTraceManager mMockTraceManager;
@Captor
private ArgumentCaptor<AccessibilityGestureEvent> mGestureCaptor;
@@ -143,6 +147,7 @@
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 502f64a..fe4fed9 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.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 @@
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 @@
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 @@
}
@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 f881f04..b14c353 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.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 @@
MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
@Mock
WindowMagnificationPromptController mWindowMagnificationPromptController;
+ @Mock
+ AccessibilityManagerService mMockAccessibilityManagerService;
+ @Mock
+ AccessibilityTraceManager mMockTraceManager;
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
@@ -144,7 +149,9 @@
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 @@
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 b7f5f4d..e82adc8 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 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 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ private AccessibilityTraceManager mTraceManager;
+ @Mock
private AccessibilityManagerService mService;
@Mock
private MagnificationController.TransitionCallBack mTransitionCallBack;
@@ -112,7 +115,7 @@
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 514d16a..ef6ed88 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 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 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ AccessibilityTraceManager mTraceManager;
+ @Mock
MagnificationGestureHandler.Callback mCallback;
@Before
@@ -57,6 +61,7 @@
mMgh = new TestMagnificationGestureHandler(DISPLAY_0,
/* detectTripleTap= */true,
/* detectShortcutTrigger= */true,
+ mTraceManager,
mCallback);
}
@@ -129,8 +134,9 @@
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 c88bc3b..1638563 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.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 @@
private IWindowMagnificationConnection mConnection;
@Mock
+ private AccessibilityTraceManager mTrace;
+ @Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -57,7 +61,7 @@
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 b9498d6..6a5aae6 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 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 @@
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 a20272a..af6d40f 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 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 @@
@Mock
private Context mContext;
@Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -88,7 +91,7 @@
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 32a4774..697d102 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 @@
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 a1f1610..fbe8c89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -66,19 +66,19 @@
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;
@@ -136,7 +136,7 @@
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.Assert;
import org.junit.Before;
@@ -171,25 +171,25 @@
}
@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();
@@ -344,7 +344,7 @@
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(),
@@ -369,7 +369,7 @@
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(),
@@ -396,7 +396,7 @@
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(),
@@ -417,7 +417,7 @@
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(),
@@ -465,7 +465,7 @@
.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()));
@@ -628,7 +628,7 @@
@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 */));
@@ -644,7 +644,7 @@
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 */));
}
@@ -653,15 +653,16 @@
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 */));
}
@@ -669,13 +670,13 @@
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 */));
}
@@ -688,7 +689,7 @@
.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 {
@@ -731,7 +732,7 @@
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());
@@ -1015,8 +1016,8 @@
@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);
@@ -1151,7 +1152,7 @@
/**
* 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.
*/
@@ -1397,7 +1398,7 @@
}
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();
@@ -1449,7 +1450,8 @@
@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());
@@ -1471,7 +1473,8 @@
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");
@@ -1907,8 +1910,7 @@
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);
@@ -1940,8 +1942,7 @@
.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);
@@ -2151,7 +2152,7 @@
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");
@@ -2595,7 +2596,8 @@
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 */,
@@ -2738,6 +2740,40 @@
}
@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 741f33f..8ca14bc 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.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 @@
.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 @@
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 @@
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 @@
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 @@
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 31d4612..af21e02 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_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 d5628fc..c4faaa3 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.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 @@
.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 d498d46..fb71dcb 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 @@
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 03304bb..3741d49 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.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.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.RoundedCorners;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
-import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -109,12 +108,7 @@
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 @@
}
@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 @@
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 @@
@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 b793be7..b998088 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_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 @@
@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 @@
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));
- assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, invisibleIme, 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, return candidate when the candidate window is not dimming.
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, visibleIme, NAV_BAR_BOTTOM));
+
+ // 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 @@
// 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));
+ APPEARANCE_LIGHT_NAVIGATION_BARS, dimming));
- // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS
+ // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar));
assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar,
- imeDrawDarkNavBar));
-
- // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins.
- 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 @@
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 683ed88..3982a83 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 @@
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/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f2418c6..a8ede13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -82,7 +82,7 @@
mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
- return true;
+ return dc.getImeContainer().getConfiguration();
}).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
anyInt(), any());
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 bf3ed69..5b04c91 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 @@
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 @@
}
@Test
- public void testControlsForDispatch_dockedStackVisible() {
+ public void testControlsForDispatch_dockedTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -93,7 +94,20 @@
}
@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_MULTI_WINDOW,
+ ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+ // The app must not control any system bars.
+ assertNull(controls);
+ }
+
+ @Test
+ public void testControlsForDispatch_freeformTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -101,18 +115,6 @@
ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
- // The app must not control any bars.
- assertNull(controls);
- }
-
- @Test
- public void testControlsForDispatch_dockedDividerControllerResizing() {
- addWindow(TYPE_STATUS_BAR, "statusBar");
- addWindow(TYPE_NAVIGATION_BAR, "navBar");
- mDisplayContent.getDockedDividerController().setResizing(true);
-
- final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
-
// The app must not control any system bars.
assertNull(controls);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 647a898..609d159 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -200,28 +200,37 @@
assertTrue(mLetterbox.needsApplySurfaceChanges());
mLetterbox.applySurfaceChanges(mTransaction);
- verify(mTransaction).setAlpha(mSurfaces.top, mDarkScrimAlpha);
+ verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
}
@Test
- public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
+ public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
- assertNull(mSurfaces.behind);
+ assertNull(mSurfaces.fullWindowSurface);
}
@Test
- public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() {
+ public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
- assertNotNull(mSurfaces.behind);
+ assertNotNull(mSurfaces.fullWindowSurface);
}
@Test
- public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() {
+ public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
+ mHasWallpaperBackground = true;
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertNotNull(mSurfaces.fullWindowSurface);
+ }
+
+ @Test
+ public void testNotIntersectsOrFullyContains_cornersRounded() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
mLetterbox.applySurfaceChanges(mTransaction);
@@ -249,8 +258,8 @@
public SurfaceControl right;
private SurfaceControl.Builder mBottomBuilder;
public SurfaceControl bottom;
- private SurfaceControl.Builder mBehindBuilder;
- public SurfaceControl behind;
+ private SurfaceControl.Builder mFullWindowSurfaceBuilder;
+ public SurfaceControl fullWindowSurface;
@Override
public SurfaceControl.Builder get() {
@@ -265,8 +274,8 @@
mRightBuilder = (SurfaceControl.Builder) i.getMock();
} else if (((String) i.getArgument(0)).contains("bottom")) {
mBottomBuilder = (SurfaceControl.Builder) i.getMock();
- } else if (((String) i.getArgument(0)).contains("behind")) {
- mBehindBuilder = (SurfaceControl.Builder) i.getMock();
+ } else if (((String) i.getArgument(0)).contains("fullWindow")) {
+ mFullWindowSurfaceBuilder = (SurfaceControl.Builder) i.getMock();
}
return i.getMock();
});
@@ -281,8 +290,8 @@
right = control;
} else if (i.getMock() == mBottomBuilder) {
bottom = control;
- } else if (i.getMock() == mBehindBuilder) {
- behind = control;
+ } else if (i.getMock() == mFullWindowSurfaceBuilder) {
+ fullWindowSurface = control;
}
return control;
}).when(builder).build();
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 d017c19..1b19a28 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.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 0c6545c..7ebf775 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.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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
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 9cf29d4..8d4acbb 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 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
}
/**
- * 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 f35e85c..5989497 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.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;
@@ -130,7 +132,7 @@
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());
@@ -138,7 +140,7 @@
// 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());
}
@@ -585,7 +587,7 @@
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 @@
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.
@@ -618,7 +620,7 @@
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();
@@ -637,7 +639,7 @@
.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);
@@ -1553,6 +1555,30 @@
}
@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 b89539c..e32b2aa 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 @@
}
@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 67b273a..c45c18d 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 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 @@
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 @@
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 @@
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 0ebff1d..629e452 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 @@
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/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2dfb3a1..45e5f8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -339,6 +339,44 @@
}
@Test
+ public void testTargets_noIntermediatesToWallpaper() {
+ final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ // Make DA organized so we can check that they don't get included.
+ WindowContainer parent = wallpaperWindowToken.getParent();
+ while (parent != null && parent != mDisplayContent) {
+ if (parent.asDisplayArea() != null) {
+ parent.asDisplayArea().setOrganizer(
+ mock(android.window.IDisplayAreaOrganizer.class), true /* skipAppear */);
+ }
+ parent = parent.getParent();
+ }
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ wallpaperWindowToken.setVisibleRequested(false);
+ transition.collect(wallpaperWindowToken);
+ wallpaperWindowToken.setVisibleRequested(true);
+ wallpaperWindow.mHasSurface = true;
+ doReturn(true).when(mDisplayContent).isAttached();
+ transition.collect(mDisplayContent);
+ mDisplayContent.getWindowConfiguration().setRotation(
+ (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
+
+ ArraySet<WindowContainer> targets = Transition.calculateTargets(
+ transition.mParticipants, transition.mChanges);
+ TransitionInfo info = Transition.calculateTransitionInfo(
+ 0, 0, targets, transition.mChanges);
+ // The wallpaper is not organized, so it won't have a token; however, it will be marked
+ // as IS_WALLPAPER
+ assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
+ // Make sure no intermediate display areas were pulled in between wallpaper and display.
+ assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
+ info.getChanges().get(0).getParent());
+ }
+
+ @Test
public void testIndependent() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
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 a1f89ec..efe6538 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setBounds(null);
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, cf);
}
@@ -285,7 +287,7 @@
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 @@
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 @@
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/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index d9aa871..83cdc3ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -107,9 +107,9 @@
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -128,9 +128,9 @@
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -151,9 +151,9 @@
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
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 d6a8401..39fe952 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.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;
@@ -62,6 +61,7 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
@@ -70,7 +70,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -346,7 +345,7 @@
@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 @@
.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);
}
@@ -1256,21 +1255,24 @@
@Test
public void testStartTasksInTransaction() {
WindowContainerTransaction wct = new WindowContainerTransaction();
- Bundle testOptions = new Bundle();
- testOptions.putInt("test", 20);
+ ActivityOptions testOptions = ActivityOptions.makeBasic();
+ testOptions.setTransientLaunch();
wct.startTask(1, null /* options */);
- wct.startTask(2, testOptions);
- spyOn(mWm.mAtmService);
- doReturn(START_CANCELED).when(mWm.mAtmService).startActivityFromRecents(anyInt(), any());
+ wct.startTask(2, testOptions.toBundle());
+ spyOn(mWm.mAtmService.mTaskSupervisor);
+ doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
+ anyInt(), anyInt(), anyInt(), any());
clearInvocations(mWm.mAtmService);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
- final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(1), bundleCaptor.capture());
- assertTrue(bundleCaptor.getValue().isEmpty());
+ verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+ anyInt(), anyInt(), eq(1), any());
- verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(2), bundleCaptor.capture());
- assertEquals(20, bundleCaptor.getValue().getInt("test"));
+ final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
+ ArgumentCaptor.forClass(SafeActivityOptions.class);
+ verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+ anyInt(), anyInt(), eq(2), optionsCaptor.capture());
+ assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
}
/**
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 ed18d26..d3f2d14 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 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;
@@ -311,17 +317,17 @@
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]);
}
@@ -332,25 +338,25 @@
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 92b670e..e83db78 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 @@
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 5880899..050fd80 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_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.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.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 @@
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 @@
}
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 @@
}
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 @@
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 bcd6ed7..824f91e 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 @@
// 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 @@
"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) {