Merge changes Ib0099b0f,I4c4de052 into main
* changes:
[1/n] Enforce Shell desktop cascading in Launch Params
[0/n] Create bug fix flag for Shell initial bounds regression
diff --git a/core/api/current.txt b/core/api/current.txt
index 1312753..3da5a5c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16993,7 +16993,7 @@
method public void setFilterBitmap(boolean);
method public void setFlags(int);
method public void setFontFeatureSettings(String);
- method @FlaggedApi("com.android.text.flags.typeface_redesign_readonly") public boolean setFontVariationOverride(@Nullable String);
+ method @FlaggedApi("com.android.text.flags.typeface_redesign_readonly") public void setFontVariationOverride(@Nullable String);
method public boolean setFontVariationSettings(String);
method public void setHinting(int);
method public void setLetterSpacing(float);
@@ -23332,12 +23332,12 @@
public static final class MediaCodecInfo.CodecCapabilities {
ctor public MediaCodecInfo.CodecCapabilities();
method public static android.media.MediaCodecInfo.CodecCapabilities createFromProfileLevel(String, int, int);
- method public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
+ method @Nullable public android.media.MediaCodecInfo.AudioCapabilities getAudioCapabilities();
method public android.media.MediaFormat getDefaultFormat();
- method public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
+ method @Nullable public android.media.MediaCodecInfo.EncoderCapabilities getEncoderCapabilities();
method public int getMaxSupportedInstances();
method public String getMimeType();
- method public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
+ method @Nullable public android.media.MediaCodecInfo.VideoCapabilities getVideoCapabilities();
method public boolean isFeatureRequired(String);
method public boolean isFeatureSupported(String);
method public boolean isFormatSupported(android.media.MediaFormat);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 514a582..00ec48b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2128,6 +2128,12 @@
method public android.media.PlaybackParams setAudioStretchMode(int);
}
+ public class Utils {
+ ctor public Utils();
+ field public static final String SYNCHRONIZED_VIBRATION = "synchronized";
+ field public static final String VIBRATION_URI_PARAM = "vibration_uri";
+ }
+
public final class VolumePolicy implements android.os.Parcelable {
ctor public VolumePolicy(boolean, boolean, boolean, int);
method public int describeContents();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 69d3e8d..f9ec214 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -550,35 +550,35 @@
public static final int START_ASSISTANT_NOT_ACTIVE_SESSION = FIRST_START_FATAL_ERROR_CODE + 11;
/**
- * Result for IActivityManaqer.startActivity: the activity was started
+ * Result for IActivityManager.startActivity: the activity was started
* successfully as normal.
* @hide
*/
public static final int START_SUCCESS = FIRST_START_SUCCESS_CODE;
/**
- * Result for IActivityManaqer.startActivity: the caller asked that the Intent not
+ * Result for IActivityManager.startActivity: the caller asked that the Intent not
* be executed if it is the recipient, and that is indeed the case.
* @hide
*/
public static final int START_RETURN_INTENT_TO_CALLER = FIRST_START_SUCCESS_CODE + 1;
/**
- * Result for IActivityManaqer.startActivity: activity was started or brought forward in an
+ * Result for IActivityManager.startActivity: activity was started or brought forward in an
* existing task which was brought to the foreground.
* @hide
*/
public static final int START_TASK_TO_FRONT = FIRST_START_SUCCESS_CODE + 2;
/**
- * Result for IActivityManaqer.startActivity: activity wasn't really started, but
+ * Result for IActivityManager.startActivity: activity wasn't really started, but
* the given Intent was given to the existing top activity.
* @hide
*/
public static final int START_DELIVERED_TO_TOP = FIRST_START_SUCCESS_CODE + 3;
/**
- * Result for IActivityManaqer.startActivity: request was canceled because
+ * Result for IActivityManager.startActivity: request was canceled because
* app switches are temporarily canceled to ensure the user's last request
* (such as pressing home) is performed.
* @hide
@@ -586,7 +586,7 @@
public static final int START_SWITCHES_CANCELED = FIRST_START_NON_FATAL_ERROR_CODE;
/**
- * Result for IActivityManaqer.startActivity: a new activity was attempted to be started
+ * Result for IActivityManager.startActivity: a new activity was attempted to be started
* while in Lock Task Mode.
* @hide
*/
@@ -594,55 +594,55 @@
FIRST_START_NON_FATAL_ERROR_CODE + 1;
/**
- * Result for IActivityManaqer.startActivity: a new activity start was aborted. Never returned
+ * Result for IActivityManager.startActivity: a new activity start was aborted. Never returned
* externally.
* @hide
*/
public static final int START_ABORTED = FIRST_START_NON_FATAL_ERROR_CODE + 2;
/**
- * Flag for IActivityManaqer.startActivity: do special start mode where
+ * Flag for IActivityManager.startActivity: do special start mode where
* a new activity is launched only if it is needed.
* @hide
*/
public static final int START_FLAG_ONLY_IF_NEEDED = 1<<0;
/**
- * Flag for IActivityManaqer.startActivity: launch the app for
+ * Flag for IActivityManager.startActivity: launch the app for
* debugging.
* @hide
*/
public static final int START_FLAG_DEBUG = 1<<1;
/**
- * Flag for IActivityManaqer.startActivity: launch the app for
+ * Flag for IActivityManager.startActivity: launch the app for
* allocation tracking.
* @hide
*/
public static final int START_FLAG_TRACK_ALLOCATION = 1<<2;
/**
- * Flag for IActivityManaqer.startActivity: launch the app with
+ * Flag for IActivityManager.startActivity: launch the app with
* native debugging support.
* @hide
*/
public static final int START_FLAG_NATIVE_DEBUGGING = 1<<3;
/**
- * Flag for IActivityManaqer.startActivity: launch the app for
+ * Flag for IActivityManager.startActivity: launch the app for
* debugging and suspend threads.
* @hide
*/
public static final int START_FLAG_DEBUG_SUSPEND = 1 << 4;
/**
- * Result for IActivityManaqer.broadcastIntent: success!
+ * Result for IActivityManager.broadcastIntent: success!
* @hide
*/
public static final int BROADCAST_SUCCESS = 0;
/**
- * Result for IActivityManaqer.broadcastIntent: attempt to broadcast
+ * Result for IActivityManager.broadcastIntent: attempt to broadcast
* a sticky intent without appropriate permission.
* @hide
*/
@@ -656,20 +656,20 @@
public static final int BROADCAST_FAILED_USER_STOPPED = -2;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent type is unknown.
+ * Type for IActivityManager.getIntentSender: this PendingIntent type is unknown.
* @hide
*/
public static final int INTENT_SENDER_UNKNOWN = 0;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a sendBroadcast operation.
* @hide
*/
public static final int INTENT_SENDER_BROADCAST = 1;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a startActivity operation.
* @hide
*/
@@ -677,21 +677,21 @@
public static final int INTENT_SENDER_ACTIVITY = 2;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for an activity result operation.
* @hide
*/
public static final int INTENT_SENDER_ACTIVITY_RESULT = 3;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a startService operation.
* @hide
*/
public static final int INTENT_SENDER_SERVICE = 4;
/**
- * Type for IActivityManaqer.getIntentSender: this PendingIntent is
+ * Type for IActivityManager.getIntentSender: this PendingIntent is
* for a startForegroundService operation.
* @hide
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1a6e9b0..dc5974f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -22,14 +22,12 @@
import static android.permission.flags.Flags.shouldRegisterAttributionSource;
import static android.view.WindowManager.LayoutParams.WindowType;
-import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UiContext;
-import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
@@ -2367,47 +2365,11 @@
Log.v(TAG, "Treating renounced permission " + permission + " as denied");
return PERMISSION_DENIED;
}
- int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
+ permission);
return PermissionManager.checkPermission(permission, pid, uid, deviceId);
}
- private int resolveDeviceIdForPermissionCheck(String permission) {
- // When checking a device-aware permission on a remote device, if the permission is CAMERA
- // or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
- // device doesn't have capability fall back to checking permission on the default device.
- // Note: we only perform permission check redirection when the device id is not explicitly
- // set in the context.
- int deviceId = getDeviceId();
- if (deviceId != Context.DEVICE_ID_DEFAULT
- && !mIsExplicitDeviceId
- && PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(permission)) {
- VirtualDeviceManager virtualDeviceManager =
- getSystemService(VirtualDeviceManager.class);
- if (virtualDeviceManager == null) {
- Slog.e(
- TAG,
- "VDM is not enabled when device id is not default. deviceId = "
- + deviceId);
- } else {
- VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
- if (virtualDevice != null) {
- if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
- && !virtualDevice.hasCustomAudioInputSupport())
- || (Objects.equals(permission, Manifest.permission.CAMERA)
- && !virtualDevice.hasCustomCameraSupport())) {
- deviceId = Context.DEVICE_ID_DEFAULT;
- }
- } else {
- Slog.e(
- TAG,
- "virtualDevice is not found when device id is not default. deviceId = "
- + deviceId);
- }
- }
- }
- return deviceId;
- }
-
/** @hide */
@Override
public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
@@ -2511,7 +2473,8 @@
@Override
public int getPermissionRequestState(String permission) {
Objects.requireNonNull(permission, "Permission name can't be null");
- int deviceId = resolveDeviceIdForPermissionCheck(permission);
+ int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
+ permission);
PermissionManager permissionManager = getSystemService(PermissionManager.class);
return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
deviceId);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c573161..38141cf 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -293,6 +293,7 @@
// The test mode. This is only used to ensure that the test functions setTestMode() and
// testPropertyName() are used correctly.
+ @GuardedBy("sGlobalLock")
private static boolean sTestMode = false;
/**
@@ -668,7 +669,7 @@
// True if this handler is in test mode. If it is in test mode, then nonces are stored
// and retrieved from mTestNonce.
@GuardedBy("mLock")
- private boolean mTestMode = false;
+ private boolean mTestMode;
// This is the local value of the nonce, as last set by the NonceHandler. It is always
// updated by the setNonce() operation. The getNonce() operation returns this value in
@@ -692,6 +693,9 @@
NonceHandler(@NonNull String name) {
mName = name;
+ synchronized (sGlobalLock) {
+ mTestMode = sTestMode;
+ }
}
/**
@@ -1414,9 +1418,13 @@
/**
* Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
- * illegal to clear the test mode if the test mode is already off. The purpose is solely to
- * ensure that test clients do not forget to use the test mode properly, even though the
- * current logic does not care.
+ * illegal to clear the test mode if the test mode is already off. Enabling test mode puts
+ * all caches in the process into test mode; all nonces are initialized to UNSET and
+ * subsequent reads and writes are to process memory. This has the effect of disabling all
+ * caches that are not local to the process. Disabling test mode restores caches to normal
+ * operation.
+ * @param mode The desired test mode.
+ * @throws IllegalStateException if the supplied mode is already set.
* @hide
*/
@VisibleForTesting
@@ -1431,10 +1439,8 @@
}
}
sTestMode = mode;
- if (mode) {
- // No action when testing begins.
- } else {
- resetAfterTestLocked();
+ if (Flags.picTestMode() || !mode) {
+ setTestModeLocked(mode);
}
}
}
@@ -1445,11 +1451,11 @@
* that were not originally in test mode.
*/
@GuardedBy("sGlobalLock")
- private static void resetAfterTestLocked() {
+ private static void setTestModeLocked(boolean mode) {
for (Iterator<String> e = sHandlers.keys().asIterator(); e.hasNext(); ) {
String s = e.next();
final NonceHandler h = sHandlers.get(s);
- h.setTestMode(false);
+ h.setTestMode(mode);
}
}
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
index 2569f7b..82875eb 100644
--- a/core/java/android/app/performance.aconfig
+++ b/core/java/android/app/performance.aconfig
@@ -50,3 +50,10 @@
description: "Cache null returns from binder calls"
bug: "372923336"
}
+
+flag {
+ namespace: "system_performance"
+ name: "pic_test_mode"
+ description: "Updated test mode for PIC"
+ bug: "396173886"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 6e3e86c..e726bc9 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -236,9 +236,17 @@
* 2. The transaction message is scheduled.
* 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
* all callbacks and necessary lifecycle transitions.
+ *
+ * @return {@link RemoteException} if the transaction failed.
*/
- public void schedule() throws RemoteException {
- mClient.scheduleTransaction(this);
+ @Nullable
+ public RemoteException schedule() {
+ try {
+ mClient.scheduleTransaction(this);
+ return null;
+ } catch (RemoteException e) {
+ return e;
+ }
}
// Parcelable implementation
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 255a08c..42bef0e 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -400,3 +400,11 @@
bug: "378539511"
is_fixed_read_only: true
}
+
+flag {
+ name: "cloud_compilation_verification"
+ namespace: "art_mainline"
+ description: "Feature flag to enable the Cloud Compilation install-time verification in the package manager."
+ bug: "377474232"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 240bc4f..2e7c3be 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -719,9 +719,13 @@
/**
* Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
- * illegal to clear the test mode if the test mode is already off. The purpose is solely to
- * ensure that test clients do not forget to use the test mode properly, even though the
- * current logic does not care.
+ * illegal to clear the test mode if the test mode is already off. Enabling test mode puts
+ * all caches in the process into test mode; all nonces are initialized to UNSET and
+ * subsequent reads and writes are to process memory. This has the effect of disabling all
+ * caches that are not local to the process. Disabling test mode restores caches to normal
+ * operation.
+ * @param mode The desired test mode.
+ * @throws IllegalStateException if the supplied mode is already set.
* @hide
*/
@TestApi
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5188204..561a2c9 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -16,6 +16,7 @@
package android.permission;
+import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -2039,12 +2040,49 @@
new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
}
+ /**
+ * When checking a device-aware permission on a remote device, if the permission is CAMERA
+ * or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
+ * device doesn't have capability fall back to checking permission on the default device.
+ *
+ * @hide
+ */
+ public static int resolveDeviceIdForPermissionCheck(@NonNull Context context, int deviceId,
+ @Nullable String permission) {
+ if (deviceId == Context.DEVICE_ID_DEFAULT || !DEVICE_AWARE_PERMISSIONS.contains(
+ permission)) {
+ return Context.DEVICE_ID_DEFAULT;
+ }
+
+ VirtualDeviceManager virtualDeviceManager =
+ context.getSystemService(VirtualDeviceManager.class);
+ if (virtualDeviceManager == null) {
+ Slog.e(LOG_TAG, "VDM is not enabled when device id is not default. deviceId = "
+ + deviceId);
+ } else {
+ VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+ if (virtualDevice != null) {
+ if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
+ && !virtualDevice.hasCustomAudioInputSupport())
+ || (Objects.equals(permission, Manifest.permission.CAMERA)
+ && !virtualDevice.hasCustomCameraSupport())) {
+ deviceId = Context.DEVICE_ID_DEFAULT;
+ }
+ } else {
+ Slog.e(LOG_TAG,
+ "virtualDevice is not found when device id is not default. deviceId = "
+ + deviceId);
+ }
+ }
+ return deviceId;
+ }
+
@Nullable
private String getPersistentDeviceId(int deviceId) {
String persistentDeviceId = null;
if (deviceId == Context.DEVICE_ID_DEFAULT) {
- persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+ persistentDeviceId = PERSISTENT_DEVICE_ID_DEFAULT;
} else {
VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
VirtualDeviceManager.class);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c048d79..f50d77e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34463,7 +34463,10 @@
*/
@FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
public void reportAppJankStats(@NonNull AppJankStats appJankStats) {
- getRootView().reportAppJankStats(appJankStats);
+ View rootView = getRootView();
+ if (rootView == this) return;
+
+ rootView.reportAppJankStats(appJankStats);
}
/**
@@ -34471,6 +34474,10 @@
* @hide
*/
public @Nullable JankTracker getJankTracker() {
- return getRootView().getJankTracker();
+ View rootView = getRootView();
+ if (rootView == this) {
+ return null;
+ }
+ return rootView.getJankTracker();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7fd7be8..e157da7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -153,6 +153,7 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
+import android.app.UiModeManager;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
import android.app.servertransaction.WindowStateTransactionItem;
@@ -196,6 +197,7 @@
import android.hardware.input.InputManagerGlobal;
import android.hardware.input.InputSettings;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -472,8 +474,6 @@
@Nullable
private ContentObserver mForceInvertObserver;
- private static final int INVALID_VALUE = Integer.MIN_VALUE;
- private int mForceInvertEnabled = INVALID_VALUE;
/**
* Callback for notifying about global configuration changes.
*/
@@ -555,6 +555,8 @@
@UiContext
public final Context mContext;
+ private UiModeManager mUiModeManager;
+
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -1804,23 +1806,6 @@
}
}
- private boolean isForceInvertEnabled() {
- if (mForceInvertEnabled == INVALID_VALUE) {
- reloadForceInvertEnabled();
- }
- return mForceInvertEnabled == 1;
- }
-
- private void reloadForceInvertEnabled() {
- if (forceInvertColor()) {
- mForceInvertEnabled = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* def= */ 0,
- UserHandle.myUserId());
- }
- }
-
/**
* Register any kind of listeners if setView was success.
*/
@@ -1856,17 +1841,22 @@
mForceInvertObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- reloadForceInvertEnabled();
updateForceDarkMode();
}
};
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED
- ),
- false,
- mForceInvertObserver,
- UserHandle.myUserId());
+
+ final Uri[] urisToObserve = {
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED),
+ Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE)
+ };
+ for (Uri uri : urisToObserve) {
+ mContext.getContentResolver().registerContentObserver(
+ uri,
+ false,
+ mForceInvertObserver,
+ UserHandle.myUserId());
+ }
}
}
}
@@ -2073,21 +2063,25 @@
return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
}
- /** Returns true if force dark should be enabled according to various settings */
+ /**
+ * Determines the type of force dark to apply, considering force inversion, system night mode,
+ * and app-specific settings (including developer opt-outs).
+ *
+ * @return A {@link ForceDarkType.ForceDarkTypeDef} constant indicating the force dark type.
+ */
@VisibleForTesting
public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
if (forceInvertColor()) {
// Force invert ignores all developer opt-outs.
// We also ignore dark theme, since the app developer can override the user's preference
- // for dark mode in configuration.uiMode. Instead, we assume that the force invert
- // setting will be enabled at the same time dark theme is in the Settings app.
- if (isForceInvertEnabled()) {
+ // for dark mode in configuration.uiMode. Instead, we assume that both force invert and
+ // the system's dark theme are enabled.
+ if (getUiModeManager().getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) {
return ForceDarkType.FORCE_INVERT_COLOR_DARK;
}
}
boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
-
if (useAutoDark) {
boolean forceDarkAllowedDefault =
SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false);
@@ -9401,6 +9395,13 @@
return mAudioManager;
}
+ private UiModeManager getUiModeManager() {
+ if (mUiModeManager == null) {
+ mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ }
+ return mUiModeManager;
+ }
+
private Vibrator getSystemVibrator() {
if (mVibrator == null) {
mVibrator = mContext.getSystemService(Vibrator.class);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 196ae5e..83dc79b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -631,6 +631,12 @@
int TRANSIT_FLAG_AOD_APPEARING = (1 << 15); // 0x8000
/**
+ * Transition flag: Indicates that the task shouldn't move to front when launching the activity.
+ * @hide
+ */
+ int TRANSIT_FLAG_AVOID_MOVE_TO_FRONT = (1 << 16); // 0x10000
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -650,6 +656,7 @@
TRANSIT_FLAG_KEYGUARD_UNOCCLUDING,
TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH,
TRANSIT_FLAG_AOD_APPEARING,
+ TRANSIT_FLAG_AVOID_MOVE_TO_FRONT,
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
diff --git a/core/java/android/webkit/TEST_MAPPING b/core/java/android/webkit/TEST_MAPPING
index 3858059..c9b5476 100644
--- a/core/java/android/webkit/TEST_MAPPING
+++ b/core/java/android/webkit/TEST_MAPPING
@@ -17,15 +17,6 @@
"exclude-annotation": "android.test.FlakyTest"
}
]
- },
- {
- "name": "GtsWebViewHostTestCases",
- "keywords": ["internal"],
- "options": [
- {
- "exclude-annotation": "android.test.FlakyTest"
- }
- ]
}
]
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 14b208a..ab7a4f2 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1458,13 +1458,14 @@
public abstract void setNeedInitialFocus(boolean flag);
/**
- * Sets the priority of the Render thread. Unlike the other settings, this
+ * Sets the CPU scheduling priority of the Render thread. Unlike the other settings, this
* one only needs to be called once per process. The default value is
* {@link RenderPriority#NORMAL}.
*
* @param priority the priority
- * @deprecated It is not recommended to adjust thread priorities, and this will
- * not be supported in future versions.
+ * @deprecated This is no longer supported. See {@link WebView#setRendererPriorityPolicy} if you
+ * instead want to control how freely the system should kill the renderer process
+ * under low memory conditions.
*/
@Deprecated
public abstract void setRenderPriority(RenderPriority priority);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b666399..5e22c28 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2287,26 +2287,53 @@
public @interface RendererPriority {}
/**
- * The renderer associated with this WebView is bound with
- * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
- * {@link WebView} renderers will be strong targets for out of memory
- * killing.
+ * This is the lowest binding priority for the WebView renderer process. This is equivalent to
+ * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level {@link WebView} renderers will be
+ * frequent targets for being killed when the system is running low on memory.
*
- * Use with {@link #setRendererPriorityPolicy}.
+ * <p>If using this priority, we recommend handling the {@link
+ * WebViewClient#onRenderProcessGone} callback to recover from low memory kills as well as other
+ * types of renderer crashes.
+ *
+ * @see #setRendererPriorityPolicy
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public static final int RENDERER_PRIORITY_WAIVED = 0;
+
/**
- * The renderer associated with this WebView is bound with
- * the default priority for services.
+ * This is the medium binding priority for the WebView renderer process. This is equivalent to
+ * the standard priority used by the system for calls to {@link
+ * Context#bindService(Intent,android.content.ServiceConnection,int)} when no flags are
+ * provided. At this priority level {@link WebView} renderers will be slightly more likely
+ * targets for being killed when the system is running low on memory.
*
- * Use with {@link #setRendererPriorityPolicy}.
+ * <p>If using this priority, we recommend handling the {@link
+ * WebViewClient#onRenderProcessGone} callback to recover from low memory kills as well as other
+ * types of renderer crashes.
+ *
+ * @see #setRendererPriorityPolicy
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public static final int RENDERER_PRIORITY_BOUND = 1;
+
/**
- * The renderer associated with this WebView is bound with
- * {@link Context#BIND_IMPORTANT}.
+ * This is the highest binding priority for the WebView renderer process, and the default value
+ * WebView uses to bind to the renderer process. This is equivalent to {@link
+ * Context#BIND_IMPORTANT}. At this priority level {@link WebView} renderers are less likely to
+ * be killed when the system is running low on memory and will have the same priority as your
+ * app's main process.
*
- * Use with {@link #setRendererPriorityPolicy}.
+ * <p>It's still possible for the renderer process to be killed when the system is running low
+ * on memory, however specifying this priority makes this situation less likely. It's also still
+ * possible for the renderer process to crash for other reasons. You may optionally still choose
+ * to handle {@link WebViewClient#onRenderProcessGone} in order to recover from a killed or
+ * crashed renderer process.
+ *
+ * @see #setRendererPriorityPolicy
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public static final int RENDERER_PRIORITY_IMPORTANT = 2;
@@ -2316,7 +2343,7 @@
* process renderer should be considered to be a target for OOM
* killing.
*
- * Because a renderer can be associated with more than one
+ * <p>Because a renderer can be associated with more than one
* WebView, the final priority it is computed as the maximum of
* any attached WebViews. When a WebView is destroyed it will
* cease to be considerered when calculating the renderer
@@ -2324,7 +2351,7 @@
* the priority of the renderer will be reduced to
* {@link #RENDERER_PRIORITY_WAIVED}.
*
- * The default policy is to set the priority to
+ * <p>The default policy is to set the priority to
* {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
* and this should not be changed unless the caller also handles
* renderer crashes with
@@ -2338,6 +2365,8 @@
* when this WebView is not visible, it will be treated as
* if it had requested a priority of
* {@link #RENDERER_PRIORITY_WAIVED}.
+ * @see #getRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
public void setRendererPriorityPolicy(
@RendererPriority int rendererRequestedPriority,
@@ -2349,6 +2378,8 @@
* Get the requested renderer priority for this WebView.
*
* @return the requested renderer priority policy.
+ * @see #setRendererPriorityPolicy
+ * @see RenderProcessGoneDetail#rendererPriorityAtExit
*/
@InspectableProperty(hasAttributeId = false, enumMapping = {
@InspectableProperty.EnumEntry(name = "waived", value = RENDERER_PRIORITY_WAIVED),
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 8162540..696b7b8 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -61,6 +61,8 @@
ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(Flags::enableCompatUiVisibilityStatus, true),
ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX(
Flags::enableDesktopIndicatorInSeparateThreadBugfix, false),
+ ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX(
+ Flags::enableDesktopOpeningDeeplinkMinimizeAnimationBugfix, false),
ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 9f768f0..f2efa20 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -152,4 +152,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "cleanup_dispatch_pending_transactions_remote_exception"
+ description: "Refactor to cleanup for RemoteException from dispatchPendingTransactions"
+ bug: "323801078"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index ac78e87..f75a72d 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -414,16 +414,23 @@
@Test
@DisabledOnRavenwood(reason = "SystemProperties doesn't have permission check")
public void testPermissionFailure() {
- // Create a cache that will write a system nonce.
- TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
try {
- // Invalidate the cache, which writes the system property. There must be a permission
- // failure.
- sysCache.invalidateCache();
- fail("expected permission failure");
- } catch (RuntimeException e) {
- // The expected exception is a bare RuntimeException. The test does not attempt to
- // validate the text of the exception message.
+ // Disable the test mode for this test, but ensure that it will be enabled when the
+ // test exits.
+ PropertyInvalidatedCache.setTestMode(false);
+ // Create a cache that will write a system nonce.
+ TestCache sysCache = new TestCache(MODULE_SYSTEM, "mode1");
+ try {
+ // Invalidate the cache, which writes the system property. There must be a
+ // permission failure.
+ sysCache.invalidateCache();
+ fail("expected permission failure");
+ } catch (RuntimeException e) {
+ // The expected exception is a bare RuntimeException. The test does not attempt
+ // to validate the text of the exception message.
+ }
+ } finally {
+ PropertyInvalidatedCache.setTestMode(true);
}
}
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index 74b32a1..791ec5d 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -452,22 +452,28 @@
assertTrue(ec.isDisabled());
}
-
// Verify that invalidating the cache from an app process would fail due to lack of permissions.
@Test
@android.platform.test.annotations.DisabledOnRavenwood(
reason = "SystemProperties doesn't have permission check")
public void testPermissionFailure() {
- // Create a cache that will write a system nonce.
- TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
try {
- // Invalidate the cache, which writes the system property. There must be a permission
- // failure.
- sysCache.invalidateCache();
- fail("expected permission failure");
- } catch (RuntimeException e) {
- // The expected exception is a bare RuntimeException. The test does not attempt to
- // validate the text of the exception message.
+ // Disable test mode for this test. Guarantee that the mode is enabled before the
+ // test exits.
+ IpcDataCache.setTestMode(false);
+ // Create a cache that will write a system nonce.
+ TestCache sysCache = new TestCache(IpcDataCache.MODULE_SYSTEM, "mode1");
+ try {
+ // Invalidate the cache, which writes the system property. There must be a
+ // permission failure.
+ sysCache.invalidateCache();
+ fail("expected permission failure");
+ } catch (RuntimeException e) {
+ // The expected exception is a bare RuntimeException. The test does not attempt
+ // to validate the text of the exception message.
+ }
+ } finally {
+ IpcDataCache.setTestMode(true);
}
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index f5d1e7a..39f3d33 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.app.UiModeManager.MODE_NIGHT_NO;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.util.SequenceUtils.getInitSeq;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
@@ -67,8 +69,10 @@
import android.annotation.NonNull;
import android.app.Instrumentation;
import android.app.UiModeManager;
+import android.app.UiModeManager.ForceInvertType;
import android.content.Context;
import android.graphics.ForceDarkType;
+import android.graphics.ForceDarkType.ForceDarkTypeDef;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
@@ -93,9 +97,12 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.TestUtils;
import com.android.cts.input.BlockingQueueEventVerifier;
import com.android.window.flags.Flags;
+import com.google.common.truth.Expect;
+
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.AfterClass;
@@ -124,6 +131,8 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final Expect mExpect = Expect.create();
private ViewRootImpl mViewRootImpl;
private View mView;
@@ -1507,49 +1516,34 @@
}
@Test
- public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
- ShellIdentityUtils.invokeWithShellPermissions(() -> {
- Settings.Secure.putInt(
- sContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* value= */ 0
- );
- var uiModeManager = sContext.getSystemService(UiModeManager.class);
- uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
- });
+ @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+ public void updateConfiguration_returnsExpectedForceDarkMode() {
+ waitForSystemNightModeActivated(true);
- sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
- assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
+ waitForSystemNightModeActivated(false);
+
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
}
@Test
- public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
- ShellIdentityUtils.invokeWithShellPermissions(() -> {
- Settings.Secure.putInt(
- sContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* value= */ 1
- );
- var uiModeManager = sContext.getSystemService(UiModeManager.class);
- uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
- });
-
- sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
-
- assertThat(mViewRootImpl.determineForceDarkType())
- .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK);
- }
-
- @Test
+ @EnableFlags(FLAG_FORCE_INVERT_COLOR)
public void forceInvertOffForceDarkOff_forceDarkModeDisabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
Settings.Secure.putInt(
sContext.getContentResolver(),
@@ -1562,15 +1556,14 @@
});
sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
}
@Test
+ @EnableFlags(FLAG_FORCE_INVERT_COLOR)
public void forceInvertOffForceDarkOn_forceDarkModeEnabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
Settings.Secure.putInt(
sContext.getContentResolver(),
@@ -1582,8 +1575,7 @@
});
sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
}
@@ -1790,4 +1782,39 @@
() -> view.getViewTreeObserver().removeOnDrawListener(listener));
}
}
+
+ private void waitForSystemNightModeActivated(boolean active) {
+ ShellIdentityUtils.invokeWithShellPermissions(() ->
+ sInstrumentation.runOnMainSync(() -> {
+ var uiModeManager = sContext.getSystemService(UiModeManager.class);
+ uiModeManager.setNightModeActivated(active);
+ }));
+ sInstrumentation.waitForIdleSync();
+ }
+
+ private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled,
+ @ForceInvertType int expectedForceInvertType,
+ @ForceDarkTypeDef int expectedForceDarkType) {
+ var uiModeManager = sContext.getSystemService(UiModeManager.class);
+ ShellIdentityUtils.invokeWithShellPermissions(() -> {
+ uiModeManager.setApplicationNightMode(
+ isAppInNightMode ? MODE_NIGHT_YES : MODE_NIGHT_NO);
+ Settings.Secure.putInt(
+ sContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+ isForceInvertEnabled ? 1 : 0);
+ });
+
+ sInstrumentation.runOnMainSync(() ->
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
+ try {
+ TestUtils.waitUntil("Waiting for force invert state changed",
+ () -> (uiModeManager.getForceInvertState() == expectedForceInvertType));
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected error trying to apply force invert state. " + e);
+ e.printStackTrace();
+ }
+
+ mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType);
+ }
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index a0ca098..05e23a0 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2206,26 +2206,21 @@
* @param fontVariationOverride font variation override. You can pass null or empty string for
* clearing font variation override.
*
- * @return true if the provided font variation settings is valid. Otherwise returns false.
- *
+ * @throws IllegalArgumentException If given string is not a valid font variation settings
+ * format
* @see #getFontVariationSettings()
* @see #setFontVariationSettings(String)
* @see #getFontVariationOverride()
* @see FontVariationAxis
*/
@FlaggedApi(FLAG_TYPEFACE_REDESIGN_READONLY)
- public boolean setFontVariationOverride(@Nullable String fontVariationOverride) {
+ public void setFontVariationOverride(@Nullable String fontVariationOverride) {
if (Objects.equals(fontVariationOverride, mFontVariationOverride)) {
- return true;
+ return;
}
- List<FontVariationAxis> axes;
- try {
- axes = FontVariationAxis.fromFontVariationSettingsForList(fontVariationOverride);
- } catch (IllegalArgumentException e) {
- Log.i(TAG, "failed to parse font variation settings.", e);
- return false;
- }
+ List<FontVariationAxis> axes =
+ FontVariationAxis.fromFontVariationSettingsForList(fontVariationOverride);
long builderPtr = nCreateFontVariationBuilder(axes.size());
for (int i = 0; i < axes.size(); ++i) {
FontVariationAxis axis = axes.get(i);
@@ -2234,7 +2229,6 @@
}
nSetFontVariationOverride(mNativePaint, builderPtr);
mFontVariationOverride = fontVariationOverride;
- return true;
}
/**
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 305fcdd..be22402 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
@@ -117,6 +117,7 @@
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
@@ -795,7 +796,7 @@
* Update bubble bar location and trigger and update to listeners
*/
public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
if (isShowingAsBubbleBar()) {
updateExpandedViewForBubbleBarLocation(bubbleBarLocation, source);
BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate();
@@ -805,7 +806,7 @@
}
private void updateExpandedViewForBubbleBarLocation(BubbleBarLocation bubbleBarLocation,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
if (isShowingAsBubbleBar()) {
BubbleBarLocation previousLocation = mBubblePositioner.getBubbleBarLocation();
mBubblePositioner.setBubbleBarLocation(bubbleBarLocation);
@@ -818,7 +819,7 @@
private void logBubbleBarLocationIfChanged(BubbleBarLocation location,
BubbleBarLocation previous,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
if (mLayerView == null) {
return;
}
@@ -830,25 +831,25 @@
return;
}
switch (source) {
- case BubbleBarLocation.UpdateSource.DRAG_BAR:
- case BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR:
+ case UpdateSource.DRAG_BAR:
+ case UpdateSource.A11Y_ACTION_BAR:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR);
break;
- case BubbleBarLocation.UpdateSource.DRAG_BUBBLE:
- case BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE:
+ case UpdateSource.DRAG_BUBBLE:
+ case UpdateSource.A11Y_ACTION_BUBBLE:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE);
break;
- case BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW:
- case BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW:
+ case UpdateSource.DRAG_EXP_VIEW:
+ case UpdateSource.A11Y_ACTION_EXP_VIEW:
// TODO(b/349845968): move logging from BubbleBarLayerView to here
break;
- case BubbleBarLocation.UpdateSource.APP_ICON_DRAG:
+ case UpdateSource.APP_ICON_DRAG:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_APP_ICON_DROP
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_APP_ICON_DROP);
break;
- case BubbleBarLocation.UpdateSource.DRAG_TASK:
+ case UpdateSource.DRAG_TASK:
mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_TASK
: BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_TASK);
break;
@@ -872,10 +873,7 @@
if (bubbleBarLocation == null) return;
if (isShowingAsBubbleBar() && BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
mBubbleStateListener.onDragItemOverBubbleBarDragZone(bubbleBarLocation);
- ensureBubbleViewsAndWindowCreated();
- if (mLayerView != null) {
- mLayerView.showBubbleBarExtendedViewDropTarget(bubbleBarLocation);
- }
+ showBubbleBarExpandedViewDropTarget(bubbleBarLocation);
}
}
@@ -921,6 +919,13 @@
return result;
}
+ private void showBubbleBarExpandedViewDropTarget(BubbleBarLocation bubbleBarLocation) {
+ ensureBubbleViewsAndWindowCreated();
+ if (mLayerView != null) {
+ mLayerView.showBubbleBarExtendedViewDropTarget(bubbleBarLocation);
+ }
+ }
+
private void hideBubbleBarExpandedViewDropTarget() {
if (mLayerView != null) {
mLayerView.hideBubbleBarExpandedViewDropTarget();
@@ -1541,20 +1546,9 @@
public void expandStackAndSelectBubble(ShortcutInfo info,
@Nullable BubbleBarLocation bubbleBarLocation) {
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
- BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
- if (updateLocation != null) {
- updateExpandedViewForBubbleBarLocation(updateLocation,
- BubbleBarLocation.UpdateSource.APP_ICON_DRAG);
- }
Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
- if (b.isInflated()) {
- mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
- } else {
- b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false,
- updateLocation);
- }
+ expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
}
/**
@@ -1562,16 +1556,12 @@
*
* @param intent the intent for the bubble.
*/
- public void expandStackAndSelectBubble(Intent intent, UserHandle user) {
+ public void expandStackAndSelectBubble(Intent intent, UserHandle user,
+ @Nullable BubbleBarLocation bubbleBarLocation) {
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
Bubble b = mBubbleData.getOrCreateBubble(intent, user); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
- if (b.isInflated()) {
- mBubbleData.setSelectedBubbleAndExpandStack(b);
- } else {
- b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
- inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
- }
+ expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
}
/**
@@ -1583,14 +1573,19 @@
public void expandStackAndSelectBubble(PendingIntent pendingIntent, UserHandle user,
@Nullable BubbleBarLocation bubbleBarLocation) {
if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
- BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
- if (updateLocation != null) {
- updateExpandedViewForBubbleBarLocation(updateLocation,
- BubbleBarLocation.UpdateSource.APP_ICON_DRAG);
- }
- Bubble b = mBubbleData.getOrCreateBubble(pendingIntent, user);
+ Bubble b = mBubbleData.getOrCreateBubble(pendingIntent, user); // Removes from overflow
ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - pendingIntent=%s",
pendingIntent);
+ expandStackAndSelectAppBubble(b, bubbleBarLocation, UpdateSource.APP_ICON_DRAG);
+ }
+
+ private void expandStackAndSelectAppBubble(Bubble b,
+ @Nullable BubbleBarLocation bubbleBarLocation, @UpdateSource int source) {
+ if (!BubbleAnythingFlagHelper.enableCreateAnyBubble()) return;
+ BubbleBarLocation updateLocation = isShowingAsBubbleBar() ? bubbleBarLocation : null;
+ if (updateLocation != null) {
+ updateExpandedViewForBubbleBarLocation(updateLocation, source);
+ }
if (b.isInflated()) {
mBubbleData.setSelectedBubbleAndExpandStack(b, updateLocation);
} else {
@@ -1623,7 +1618,7 @@
}
} else {
if (location != null) {
- setBubbleBarLocation(location, BubbleBarLocation.UpdateSource.DRAG_TASK);
+ setBubbleBarLocation(location, UpdateSource.DRAG_TASK);
}
b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
// Lazy init stack view when a bubble is created
@@ -2840,14 +2835,16 @@
}
@Override
- public void showShortcutBubble(ShortcutInfo info) {
+ public void showShortcutBubble(ShortcutInfo info, @Nullable BubbleBarLocation location) {
mMainExecutor.execute(() -> mController
- .expandStackAndSelectBubble(info, /* bubbleBarLocation = */ null));
+ .expandStackAndSelectBubble(info, location));
}
@Override
- public void showAppBubble(Intent intent, UserHandle user) {
- mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(intent, user));
+ public void showAppBubble(Intent intent, UserHandle user,
+ @Nullable BubbleBarLocation location) {
+ mMainExecutor.execute(
+ () -> mController.expandStackAndSelectBubble(intent, user, location));
}
@Override
@@ -2900,7 +2897,7 @@
@Override
public void setBubbleBarLocation(BubbleBarLocation location,
- @BubbleBarLocation.UpdateSource int source) {
+ @UpdateSource int source) {
mMainExecutor.execute(() ->
mController.setBubbleBarLocation(location, source));
}
@@ -2921,6 +2918,17 @@
}
});
}
+
+ @Override
+ public void showDropTarget(boolean show, BubbleBarLocation location) {
+ mMainExecutor.execute(() -> {
+ if (show) {
+ showBubbleBarExpandedViewDropTarget(location);
+ } else {
+ hideBubbleBarExpandedViewDropTarget();
+ }
+ });
+ }
}
private class BubblesImpl implements Bubbles {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 0a4d79a..ae1b407 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -51,9 +51,11 @@
oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
- oneway void showShortcutBubble(in ShortcutInfo info) = 12;
+ oneway void showShortcutBubble(in ShortcutInfo info, in @nullable BubbleBarLocation location) = 12;
- oneway void showAppBubble(in Intent intent, in UserHandle user) = 13;
+ oneway void showAppBubble(in Intent intent, in UserHandle user, in @nullable BubbleBarLocation location) = 13;
oneway void showExpandedView() = 14;
+
+ oneway void showDropTarget(in boolean show, in @nullable BubbleBarLocation location) = 15;
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 6034299..70a648f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -35,7 +35,7 @@
desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
return
}
- if (isFreeformTask(taskInfo)) {
+ if (isFreeformTask(taskInfo) && !desktopRepository.isActiveTask(taskInfo.taskId)) {
desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
}
}
@@ -44,23 +44,33 @@
logD("onTaskChanging for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
-
// TODO: b/394281403 - with multiple desks, it's possible to have a non-freeform task
// inside a desk, so this should be decoupled from windowing mode.
// Also, changes in/out of desks are handled by the [DesksTransitionObserver], which has
// more specific information about the desk involved in the transition, which might be
// more accurate than assuming it's always the default/active desk in the display, as this
// method does.
- // Case 1: Freeform task is changed in Desktop Mode.
- if (isFreeformTask(taskInfo)) {
- if (taskInfo.isVisible) {
- desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ // Case 1: When the task change is from a task in the desktop repository which is now
+ // fullscreen,
+ // remove the task from the desktop repository since it is no longer a freeform task.
+ if (!isFreeformTask(taskInfo)) {
+ if (desktopRepository.isActiveTask(taskInfo.taskId)) {
+ desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
- desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
- } else {
- // Case 2: Freeform task is changed outside Desktop Mode.
- desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
+ } else { // Task change is a freeform task
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) {
+ // Case 2: When the task change is a freeform visible task, but the task is not
+ // yet active in the desktop repository, adds task to desktop repository.
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ } else {
+ // Case 3: When the task change is a freeform task which already exists as an active
+ // task in the desktop repository, updates the task state.
+ desktopRepository.updateTask(
+ taskInfo.displayId,
+ taskInfo.taskId,
+ taskInfo.isVisible,
+ )
+ }
}
}
@@ -82,14 +92,22 @@
logD("onTaskMovingToFront for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
val desktopRepository: DesktopRepository =
desktopUserRepositories.getProfile(taskInfo.userId)
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
- if (!isFreeformTask(taskInfo)) {
+ // When the task change is from a task in the desktop repository which is now fullscreen,
+ // remove the task from the desktop repository since it is no longer a freeform task.
+ if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
desktopRepository.removeTask(taskInfo.displayId, taskInfo.taskId)
}
- desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ if (isFreeformTask(taskInfo)) {
+ // If the task is already active in the repository, then it only moves the task to the
+ // front.
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ }
}
override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
+ val desktopRepository: DesktopRepository =
+ desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
logD("onTaskMovingToBack for taskId=%d, displayId=%d", taskInfo.taskId, taskInfo.displayId)
// TODO: b/367268953 - Connect this with DesktopRepository.
}
@@ -101,7 +119,7 @@
if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
// TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
if (
- !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
+ !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue ||
desktopRepository.isClosingTask(taskInfo.taskId)
) {
// A task that's vanishing should be removed:
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 9302347..a3a808d 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
@@ -2080,8 +2080,8 @@
Math.max(topLeftBounds.top, 0);
bottomRightBounds.right =
Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
- bottomRightBounds.top =
- Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+ bottomRightBounds.bottom =
+ Math.min(bottomRightBounds.bottom, mSplitLayout.getDisplayHeight());
// TODO (b/349828130): Can change to getState() fully after brief soak time.
if (mSplitState.get() != currentSnapPosition) {
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 3652a16..bf58003 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
@@ -95,7 +95,6 @@
import android.util.ArrayMap;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
@@ -835,9 +834,8 @@
a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter, userId);
}
} 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);
- a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+ // Apply end state directly by default.
+ return null;
} else if (type == TRANSIT_RELAUNCH) {
a = mTransitionAnimation.createRelaunchAnimation(endBounds, mInsets, endBounds);
} else if (overrideType == ANIM_CUSTOM
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index 4c443d7..d73d08c 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -498,9 +498,9 @@
it.remove(VisibleLayersShownMoreThanOneConsecutiveEntry())
} +
listOf(
- AppWindowOnTopAtStart(DESKTOP_MODE_APP),
AppWindowBecomesInvisible(DESKTOP_MODE_APP),
AppWindowOnTopAtEnd(LAUNCHER),
+ AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER),
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
)
val OPEN_UNLIMITED_APPS =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index d054de4..cc37c44 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -26,7 +26,6 @@
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
-import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
import android.view.WindowManager.TRANSIT_CHANGE
@@ -43,6 +42,8 @@
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
import com.android.wm.shell.transition.Transitions
import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,7 +63,7 @@
* Usage: atest WMShellUnitTests:DesktopDisplayModeControllerTest
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(TestParameterInjector::class)
class DesktopDisplayModeControllerTest : ShellTestCase() {
private val transitions = mock<Transitions>()
private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
@@ -143,41 +144,24 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay_flagDisabled() {
+ fun displayWindowingModeSwitchOnDisplayConnected_flagDisabled(
+ @TestParameter param: ModeSwitchTestCase
+ ) {
testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
+ param.defaultWindowingMode,
+ param.extendedDisplayEnabled,
+ // When the flag is disabled, never switch.
expectToSwitch = false,
)
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+ fun displayWindowingModeSwitchOnDisplayConnected(@TestParameter param: ModeSwitchTestCase) {
testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = false,
- expectToSwitch = false,
- )
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
- expectToSwitch = true,
- )
- }
-
- @Test
- @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING)
- fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FREEFORM,
- extendedDisplayEnabled = true,
- expectToSwitch = false,
+ param.defaultWindowingMode,
+ param.extendedDisplayEnabled,
+ param.expectToSwitchByDefault,
)
}
@@ -249,7 +233,34 @@
}
}
- private companion object {
+ companion object {
const val EXTERNAL_DISPLAY_ID = 100
+
+ enum class ModeSwitchTestCase(
+ val defaultWindowingMode: Int,
+ val extendedDisplayEnabled: Boolean,
+ val expectToSwitchByDefault: Boolean,
+ ) {
+ FULLSCREEN_DISPLAY(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = true,
+ expectToSwitchByDefault = true,
+ ),
+ FULLSCREEN_DISPLAY_MIRRORING(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = false,
+ expectToSwitchByDefault = false,
+ ),
+ FREEFORM_DISPLAY(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = true,
+ expectToSwitchByDefault = false,
+ ),
+ FREEFORM_DISPLAY_MIRRORING(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = false,
+ expectToSwitchByDefault = false,
+ ),
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 50590f0..6b0ee5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -56,7 +56,7 @@
}
@Test
- fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
+ fun onTaskOpening_fullscreenTask_nonActiveDesktopTask_noop() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
@@ -68,7 +68,7 @@
}
@Test
- fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
+ fun onTaskOpening_fullscreenTask_taskIsActiveInDesktopRepo_removesTaskFromDesktopRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -78,8 +78,19 @@
}
@Test
- fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
+ fun onTaskOpening_freeformTask_activeInDesktopRepository_noop() {
val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+
+ desktopTaskChangeListener.onTaskOpening(task)
+
+ verify(desktopUserRepositories.current, never())
+ .addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskOpening_freeformTask_notActiveInDesktopRepo_addsTaskToRepository() {
+ val task = createFreeformTask().apply { isVisible = false }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
desktopTaskChangeListener.onTaskOpening(task)
@@ -88,17 +99,7 @@
}
@Test
- fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
- val task = createFreeformTask().apply { isVisible = false }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
-
- desktopTaskChangeListener.onTaskOpening(task)
-
- verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
- }
-
- @Test
- fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+ fun onTaskChanging_fullscreenTask_activeInDesktopRepository_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -108,7 +109,27 @@
}
@Test
- fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
+ fun onTaskChanging_fullscreenTask_nonActiveInDesktopRepo_noop() {
+ val task = createFullscreenTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ fun onTaskChanging_freeformTask_nonActiveTaskInDesktopRepo_addsTaskToDesktopRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskChanging(task)
+
+ verify(desktopUserRepositories.current).addTask(task.displayId, task.taskId, task.isVisible)
+ }
+
+ @Test
+ fun onTaskChanging_freeformTask_activeVisibleTaskInDesktopRepo_updatesTaskVisibility() {
val task = createFreeformTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -119,7 +140,7 @@
}
@Test
- fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
+ fun onTaskChanging_freeformTask_activeNonVisibleTask_updatesTaskVisibility() {
val task = createFreeformTask().apply { isVisible = false }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -130,7 +151,7 @@
}
@Test
- fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
+ fun onTaskMovingToFront_fullscreenTask_activeTaskInDesktopRepo_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
@@ -140,9 +161,18 @@
}
@Test
- fun onTaskMovingToFront_freeformTaskOutsideDesktop_addsTaskToRepo() {
+ fun onTaskMovingToFront_fullscreenTask_nonActiveTaskInDesktopRepo_noop() {
val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId)).thenReturn(false)
+
+ desktopTaskChangeListener.onTaskMovingToFront(task)
+
+ verify(desktopUserRepositories.current, never()).removeTask(task.displayId, task.taskId)
+ }
+
+ @Test
+ fun onTaskMovingToFront_freeformTask_addsTaskToRepo() {
+ val task = createFreeformTask().apply { isVisible = true }
desktopTaskChangeListener.onTaskMovingToFront(task)
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index fb89973..66de8c7 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -27,8 +27,6 @@
import android.telephony.emergency.EmergencyNumber;
import android.util.Log;
-import com.android.internal.annotations.KeepForWeakReference;
-
import java.util.concurrent.TimeUnit;
/**
@@ -94,7 +92,6 @@
// The internal implementation of TelephonyManager uses WeakReference so we have to keep a
// reference here.
- @KeepForWeakReference
private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();
private final EmergencyCallCallback mEmergencyCallCallback;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9bb31d0..4e86eac 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1223,8 +1223,14 @@
private static final String TAG = "CodecCapabilities";
// NEW-STYLE CAPABILITIES
+ // Capabilities for an audio codec.
+ @Nullable
private AudioCapabilities mAudioCaps;
+ // Capabilities for a video codec.
+ @Nullable
private VideoCapabilities mVideoCaps;
+ // Capabilities specific to an encoder (vs. a decoder).
+ @Nullable
private EncoderCapabilities mEncoderCaps;
private MediaFormat mDefaultFormat;
@@ -1262,6 +1268,7 @@
/**
* Returns the audio capabilities or {@code null} if this is not an audio codec.
*/
+ @Nullable
public AudioCapabilities getAudioCapabilities() {
return mAudioCaps;
}
@@ -1273,6 +1280,7 @@
/**
* Returns the encoding capabilities or {@code null} if this is not an encoder.
*/
+ @Nullable
public EncoderCapabilities getEncoderCapabilities() {
return mEncoderCaps;
}
@@ -1284,6 +1292,7 @@
/**
* Returns the video capabilities or {@code null} if this is not a video codec.
*/
+ @Nullable
public VideoCapabilities getVideoCapabilities() {
return mVideoCaps;
}
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index d6e27b0..9db527b 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -61,16 +63,30 @@
*
* @hide
*/
+@TestApi
+@SuppressLint({"UnflaggedApi", "StaticUtils"}) // Test API
public class Utils {
private static final String TAG = "Utils";
+ /** @hide
+ * The vibration uri key parameter
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi") // Test API
public static final String VIBRATION_URI_PARAM = "vibration_uri";
+ /** @hide
+ * Indicates the synchronized vibration
+ */
+ @TestApi
+ @SuppressLint("UnflaggedApi") // Test API
public static final String SYNCHRONIZED_VIBRATION = "synchronized";
/**
* Sorts distinct (non-intersecting) range array in ascending order.
* @throws java.lang.IllegalArgumentException if ranges are not distinct
+ *
+ * @hide
*/
public static <T extends Comparable<? super T>> void sortDistinctRanges(Range<T>[] ranges) {
Arrays.sort(ranges, new Comparator<Range<T>>() {
@@ -92,6 +108,8 @@
* @param one a sorted set of non-intersecting ranges in ascending order
* @param another another sorted set of non-intersecting ranges in ascending order
* @return the intersection of the two sets, sorted in ascending order
+ *
+ * @hide
*/
public static <T extends Comparable<? super T>>
Range<T>[] intersectSortedDistinctRanges(Range<T>[] one, Range<T>[] another) {
@@ -124,6 +142,8 @@
* @return if the value is in one of the ranges, it returns the index of that range. Otherwise,
* the return value is {@code (-1-index)} for the {@code index} of the range that is
* immediately following {@code value}.
+ *
+ * @hide
*/
public static <T extends Comparable<? super T>>
int binarySearchDistinctRanges(Range<T>[] ranges, T value) {
@@ -358,6 +378,8 @@
* @param fileName desired name for the file.
* @param mimeType MIME type of the file to create.
* @return the File object in the storage, or null if an error occurs.
+ *
+ * @hide
*/
public static File getUniqueExternalFile(Context context, String subdirectory, String fileName,
String mimeType) {
@@ -676,6 +698,8 @@
* Must match the implementation of BluetoothUtils.toAnonymizedAddress()
* @param address MAC address to be anonymized
* @return anonymized MAC address
+ *
+ * @hide
*/
public static @Nullable String anonymizeBluetoothAddress(@Nullable String address) {
if (address == null) {
@@ -693,6 +717,8 @@
* @param deviceType the internal type of the audio device
* @param address MAC address to be anonymized
* @return anonymized MAC address
+ *
+ * @hide
*/
public static @Nullable String anonymizeBluetoothAddress(
int deviceType, @Nullable String address) {
@@ -707,6 +733,8 @@
*
* @param context the {@link Context}
* @return {@code true} if the device supports ringtone vibration
+ *
+ * @hide
*/
public static boolean isRingtoneVibrationSettingsSupported(Context context) {
final Resources res = context.getResources();
@@ -719,6 +747,8 @@
*
* @param ringtoneUri the ringtone Uri
* @return {@code true} if the Uri has vibration parameter
+ *
+ * @hide
*/
public static boolean hasVibration(Uri ringtoneUri) {
if (ringtoneUri == null) {
@@ -734,6 +764,8 @@
* @param ringtoneUri the ringtone Uri
* @return parsed {@link Uri} of vibration parameter, {@code null} if the vibration parameter
* is not found.
+ *
+ * @hide
*/
public static @Nullable Uri getVibrationUri(Uri ringtoneUri) {
if (ringtoneUri == null) {
@@ -751,6 +783,8 @@
*
* @param vibrator the vibrator to resolve the vibration file
* @param vibrationUri the vibration file Uri to represent a vibration
+ *
+ * @hide
*/
@SuppressWarnings("FlaggedApi") // VibrationXmlParser is available internally as hidden APIs.
public static VibrationEffect parseVibrationEffect(Vibrator vibrator, Uri vibrationUri) {
diff --git a/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING
index 3858059..c9b5476 100644
--- a/native/webview/TEST_MAPPING
+++ b/native/webview/TEST_MAPPING
@@ -17,15 +17,6 @@
"exclude-annotation": "android.test.FlakyTest"
}
]
- },
- {
- "name": "GtsWebViewHostTestCases",
- "keywords": ["internal"],
- "options": [
- {
- "exclude-annotation": "android.test.FlakyTest"
- }
- ]
}
]
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
index 6221659..3db0ac6 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
@@ -41,6 +41,7 @@
android:id="@+id/action_bar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
+ android:layout_marginStart="@dimen/settingslib_expressive_space_extrasmall4"
android:theme="?android:attr/actionBarTheme"
android:transitionName="shared_element_view"
app:layout_collapseMode="pin"/>
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 04df308..7348920 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -8,6 +8,7 @@
evanlaird@google.com
jiannan@google.com
juliacr@google.com
+millchen@google.com
ykhung@google.com
# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt
new file mode 100644
index 0000000..5035542
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/NormalPaddingMixin.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.settingslib.widget
+
+/**
+ * A base interface to indicate that a Preference should have normal paddings.
+ *
+ * Preferences implementing this interface will be treated as has normal paddings both inside and
+ * outside.
+ */
+interface NormalPaddingMixin
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index a04fce7..2672787 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -177,14 +177,32 @@
val v = holder.itemView
// Update padding
if (SettingsThemeHelper.isExpressiveTheme(context)) {
- val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
- val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+ val (paddingStart, paddingEnd) = getStartEndPadding(position, backgroundRes)
v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+ v.clipToOutline = backgroundRes != 0
}
// Update background
v.setBackgroundResource(backgroundRes)
}
+ private fun getStartEndPadding(position: Int, backgroundRes: Int): Pair<Int, Int> {
+ val item = getItem(position)
+ return when {
+ // This item handles edge to edge itself
+ item is NormalPaddingMixin && item is GroupSectionDividerMixin -> 0 to 0
+
+ // According to mappingPreferenceGroup(), backgroundRes == 0 means this item is
+ // GroupSectionDividerMixin or PreferenceCategory, which is design to have normal
+ // padding.
+ // NormalPaddingMixin items are also designed to have normal padding.
+ backgroundRes == 0 || item is NormalPaddingMixin ->
+ mNormalPaddingStart to mNormalPaddingEnd
+
+ // Other items are suppose to have group padding.
+ else -> mGroupPaddingStart to mGroupPaddingEnd
+ }
+ }
+
@DrawableRes
protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
return getRoundCornerDrawableRes(position, isSelected, false)
diff --git a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
index 4315238..fe8e8b6 100644
--- a/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
+++ b/packages/SettingsLib/SliderPreference/src/com/android/settingslib/widget/SliderPreference.java
@@ -22,6 +22,7 @@
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
@@ -72,6 +73,7 @@
private int mSliderIncrement;
private boolean mAdjustable;
private boolean mTrackingTouch;
+ private CharSequence mSliderContentDescription;
/**
* Listener reacting to the user pressing DPAD left/right keys if {@code
@@ -143,6 +145,7 @@
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutResource(R.layout.settingslib_expressive_preference_slider);
+ setSelectable(false);
TypedArray a = context.obtainStyledAttributes(
attrs, androidx.preference.R.styleable.SeekBarPreference, defStyleAttr,
@@ -265,6 +268,14 @@
} else {
mSliderIncrement = (int) (mSlider.getStepSize());
}
+ final CharSequence title = getTitle();
+ if (!TextUtils.isEmpty(mSliderContentDescription)) {
+ mSlider.setContentDescription(mSliderContentDescription);
+ } else if (!TextUtils.isEmpty(title)) {
+ mSlider.setContentDescription(title);
+ } else {
+ mSlider.setContentDescription(null);
+ }
mSlider.setValueFrom(mMin);
mSlider.setValueTo(mMax);
mSlider.setValue(mSliderValue);
@@ -273,6 +284,8 @@
mSlider.clearOnChangeListeners();
mSlider.addOnChangeListener(mChangeListener);
mSlider.setEnabled(isEnabled());
+ mSlider.setFocusable(isSelectable());
+ mSlider.setClickable(isSelectable());
// Set up slider color
mSlider.setTrackActiveTintList(mTrackActiveColor);
@@ -471,6 +484,19 @@
setValueInternal(sliderValue, true);
}
+
+ /**
+ * Sets the content description of the {@link Slider}.
+ *
+ * @param contentDescription The content description of the {@link Slider}
+ */
+ public void setSliderContentDescription(@Nullable CharSequence contentDescription) {
+ mSliderContentDescription = contentDescription;
+ if (mSlider != null) {
+ mSlider.setContentDescription(contentDescription);
+ }
+ }
+
@Override
protected void onSetInitialValue(@Nullable Object defaultValue) {
if (defaultValue == null) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 2c6d09a..2d03e2b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -23,7 +23,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
@@ -35,7 +34,6 @@
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalLockSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
-import com.android.systemui.communal.ui.compose.section.CommunalToDreamButtonSection
import com.android.systemui.communal.ui.compose.section.HubOnboardingSection
import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
@@ -61,7 +59,6 @@
private val ambientStatusBarSection: AmbientStatusBarSection,
private val communalPopupSection: CommunalPopupSection,
private val widgetSection: CommunalAppWidgetSection,
- private val communalToDreamButtonSection: CommunalToDreamButtonSection,
private val hubOnboardingSection: HubOnboardingSection,
) {
@@ -103,13 +100,11 @@
Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
)
}
- with(communalToDreamButtonSection) { Button() }
},
) { measurables, constraints ->
val communalGridMeasurable = measurables[0]
val lockIconMeasurable = measurables[1]
val bottomAreaMeasurable = measurables[2]
- val screensaverButtonMeasurable: Measurable? = measurables.getOrNull(3)
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
@@ -152,9 +147,6 @@
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
- val screensaverButtonPlaceable =
- screensaverButtonMeasurable?.measure(noMinConstraints)
-
val communalGridPlaceable =
communalGridMeasurable.measure(
noMinConstraints.copy(maxHeight = lockIconBounds.top)
@@ -166,26 +158,12 @@
val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
-
- val screensaverButtonPaddingInt = screensaverButtonPadding.roundToPx()
- screensaverButtonPlaceable?.place(
- x =
- constraints.maxWidth -
- screensaverButtonPaddingInt -
- screensaverButtonPlaceable.width,
- y =
- constraints.maxHeight -
- screensaverButtonPaddingInt -
- screensaverButtonPlaceable.height,
- )
}
}
}
}
companion object {
- private val screensaverButtonPadding: Dp = 24.dp
-
// TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
// position are sorted.
private val lockIconSize: Dp = 54.dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
deleted file mode 100644
index acaf43a..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2024 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.communal.ui.compose.section
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.MutableTransitionState
-import androidx.compose.animation.expandVertically
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.shrinkVertically
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.material3.IconButtonDefaults
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.CornerRadius
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.RoundRect
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.geometry.toRect
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import com.android.compose.PlatformIconButton
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.ui.compose.extensions.observeTaps
-import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
-import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.res.R
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.delay
-
-class CommunalToDreamButtonSection
-@Inject
-constructor(
- private val communalSettingsInteractor: CommunalSettingsInteractor,
- private val viewModelFactory: CommunalToDreamButtonViewModel.Factory,
-) {
- @Composable
- fun Button() {
- if (!communalSettingsInteractor.isV2FlagEnabled()) {
- return
- }
-
- val viewModel =
- rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
-
- if (!viewModel.shouldShowDreamButtonOnHub) {
- return
- }
-
- val buttonSize = dimensionResource(R.dimen.communal_to_dream_button_size)
-
- if (viewModel.shouldShowTooltip) {
- val tooltipVisibleState = remember { MutableTransitionState(false) }
-
- Column(
- modifier =
- Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
- observeTaps {
- if (tooltipVisibleState.isCurrentlyVisible()) {
- tooltipVisibleState.targetState = false
- }
- }
- }
- ) {
- var waitingToShowTooltip by remember { mutableStateOf(true) }
-
- LaunchedEffect(tooltipVisibleState.targetState) {
- delay(3.seconds)
- tooltipVisibleState.targetState = true
- waitingToShowTooltip = false
- }
-
- // This LaunchedEffect is used to wait for the tooltip dismiss animation to
- // complete before setting the tooltip dismissed. Otherwise, the composable would
- // be removed before the animation can start.
- LaunchedEffect(
- tooltipVisibleState.currentState,
- tooltipVisibleState.isIdle,
- waitingToShowTooltip,
- ) {
- if (
- !waitingToShowTooltip &&
- !tooltipVisibleState.currentState &&
- tooltipVisibleState.isIdle
- ) {
- viewModel.setDreamButtonTooltipDismissed()
- }
- }
-
- AnimatedVisibility(
- visibleState = tooltipVisibleState,
- enter = fadeIn() + expandVertically(expandFrom = Alignment.Bottom),
- exit = fadeOut() + shrinkVertically(shrinkTowards = Alignment.Bottom),
- ) {
- Tooltip(
- pointerOffsetDp = buttonSize.div(2),
- text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
- )
- }
-
- GoToDreamButton(
- modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
- ) {
- viewModel.onShowDreamButtonTap()
- }
- }
- } else {
- GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
- viewModel.onShowDreamButtonTap()
- }
- }
- }
-
- private fun MutableTransitionState<Boolean>.isCurrentlyVisible() = currentState && isIdle
-
- companion object {
- private val tooltipMaxWidth = 350.dp
- }
-}
-
-@Composable
-private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
- PlatformIconButton(
- modifier = modifier,
- onClick = onClick,
- iconResource = R.drawable.ic_screensaver_auto,
- contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
- colors =
- IconButtonDefaults.filledIconButtonColors(
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- ),
- )
-}
-
-@Composable
-private fun Tooltip(pointerOffsetDp: Dp, text: String) {
- Surface(
- color = MaterialTheme.colorScheme.surface,
- shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
- ) {
- Text(
- modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
- color = MaterialTheme.colorScheme.onSurface,
- text = text,
- )
- }
-
- Spacer(modifier = Modifier.height(4.dp))
-}
-
-private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {
-
- override fun createOutline(
- size: Size,
- layoutDirection: LayoutDirection,
- density: Density,
- ): Outline {
-
- val pointerSizePx = with(density) { pointerSizeDp.toPx() }
- val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
- val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
- val bubbleSize = size.copy(height = size.height - pointerSizePx)
-
- val path =
- Path().apply {
- addRoundRect(
- RoundRect(
- rect = bubbleSize.toRect(),
- topLeft = cornerRadius,
- topRight = cornerRadius,
- bottomRight = cornerRadius,
- bottomLeft = cornerRadius,
- )
- )
- addPath(
- Path().apply {
- moveTo(0f, 0f)
- lineTo(pointerSizePx / 2f, pointerSizePx)
- lineTo(pointerSizePx, 0f)
- close()
- },
- offset =
- Offset(
- x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
- y = bubbleSize.height,
- ),
- )
- }
-
- return Outline.Generic(path)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
deleted file mode 100644
index 2f3073e..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2024 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.communal.ui.viewmodel
-
-import android.content.pm.UserInfo
-import android.platform.test.annotations.EnableFlags
-import android.provider.Settings
-import android.service.dream.dreamManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.uiEventLoggerFake
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
-import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
-import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.runCurrent
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.settings.fakeUserTracker
-import com.android.systemui.statusbar.policy.batteryController
-import com.android.systemui.statusbar.policy.fake
-import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.fakeSettings
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mockito.verify
-import org.mockito.kotlin.any
-
-@SmallTest
-@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
-@RunWith(AndroidJUnit4::class)
-class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val underTest: CommunalToDreamButtonViewModel by lazy {
- kosmos.communalToDreamButtonViewModel
- }
-
- @Before
- fun setUp() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- underTest.activateIn(testScope)
- }
-
- @Test
- fun shouldShowDreamButtonOnHub_trueWhenPluggedIn() =
- with(kosmos) {
- runTest {
- batteryController.fake._isPluggedIn = true
- runCurrent()
-
- assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
- }
- }
-
- @Test
- fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
- with(kosmos) {
- runTest {
- batteryController.fake._isPluggedIn = false
-
- assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
- }
- }
-
- @Test
- fun onShowDreamButtonTap_dreamsEnabled_startsDream() =
- with(kosmos) {
- runTest {
- val currentUser = fakeUserRepository.asMainUser()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
- 1,
- currentUser.id,
- )
- runCurrent()
-
- underTest.onShowDreamButtonTap()
- runCurrent()
-
- verify(dreamManager).startDream()
- }
- }
-
- @Test
- fun onShowDreamButtonTap_dreamsDisabled_startsActivity() =
- with(kosmos) {
- runTest {
- val currentUser = fakeUserRepository.asMainUser()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
- 0,
- currentUser.id,
- )
- runCurrent()
-
- underTest.onShowDreamButtonTap()
- runCurrent()
-
- verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt())
- }
- }
-
- @Test
- fun shouldShowDreamButtonTooltip_trueWhenNotDismissedAndHubOnboardingDismissed() =
- kosmos.runTest {
- setSelectedUser(MAIN_USER)
- fakeCommunalPrefsRepository.setHubOnboardingDismissed(MAIN_USER)
- runCurrent()
-
- assertThat(underTest.shouldShowTooltip).isTrue()
- }
-
- @Test
- fun shouldShowDreamButtonTooltip_falseWhenNotDismissedAndHubOnboardingNotDismissed() =
- kosmos.runTest {
- runCurrent()
- assertThat(underTest.shouldShowTooltip).isFalse()
- }
-
- @Test
- fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
- kosmos.runTest {
- setSelectedUser(MAIN_USER)
- fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
- runCurrent()
-
- assertThat(underTest.shouldShowTooltip).isFalse()
- }
-
- @Test
- fun onShowDreamButtonTap_eventLogged() =
- with(kosmos) {
- runTest {
- underTest.onShowDreamButtonTap()
- runCurrent()
-
- assertThat(uiEventLoggerFake[0].eventId)
- .isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
- }
- }
-
- private suspend fun setSelectedUser(user: UserInfo) {
- with(kosmos.fakeUserRepository) {
- setUserInfos(listOf(user))
- setSelectedUserInfo(user)
- }
- kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
index 56cd72e..0bb4737 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimatorTest.kt
@@ -15,19 +15,22 @@
*/
package com.android.systemui.statusbar.notification
+import android.animation.AnimatorTestRule
import android.util.FloatProperty
import android.util.Property
import android.view.View
-import androidx.dynamicanimation.animation.DynamicAnimation
+
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.ViewState
import org.junit.Assert
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
@@ -50,6 +53,8 @@
return _value
}
}
+ @get:Rule
+ val animatorTestRule = AnimatorTestRule(this)
private val property: PhysicsProperty =
PhysicsProperty(R.id.scale_x_animator_tag, effectiveProperty)
private var finishListener: DynamicAnimation.OnAnimationEndListener? = null
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 2853264..fa185bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -18,6 +18,7 @@
import android.app.Flags
import android.app.NotificationChannel
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
@@ -124,6 +125,14 @@
}
@Test
+ fun testPrioritySectioner_doesNotClaim_classifiedConversation() {
+ val sectioner = coordinator.priorityPeopleSectioner
+ for (id in SYSTEM_RESERVED_IDS) {
+ assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+ }
+ }
+
+ @Test
fun testPromotesImportantConversations() {
assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
@@ -166,6 +175,14 @@
}
@Test
+ fun testAlertingSectioner_doesNotClaim_classifiedConversation() {
+ val sectioner = coordinator.peopleAlertingSectioner
+ for (id in SYSTEM_RESERVED_IDS) {
+ assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+ }
+ }
+
+ @Test
fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
// GIVEN
val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) }
@@ -186,6 +203,15 @@
@Test
@DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
+ fun testSilentSectioner_doesNotClaim_classifiedConversation() {
+ val sectioner = coordinator.peopleSilentSectioner
+ for (id in SYSTEM_RESERVED_IDS) {
+ assertFalse(sectioner.isInSection(makeClassifiedConversation(id)))
+ }
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
// GIVEN
val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
@@ -280,4 +306,17 @@
assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry))
return entry
}
+
+ private fun makeClassifiedConversation(channelId: String): NotificationEntry {
+ val channel = NotificationChannel(channelId, channelId, IMPORTANCE_LOW)
+ val entry =
+ NotificationEntryBuilder()
+ .updateRanking {
+ it.setIsConversation(true)
+ it.setShortcutInfo(mock())
+ it.setChannel(channel)
+ }
+ .build()
+ return entry
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
index 961616c..3d1fdee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
@@ -22,6 +22,8 @@
import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.testKosmos
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -32,12 +34,16 @@
@SmallTest
class ActivatableNotificationViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
// fakes
private val a11yRepo = FakeAccessibilityRepository()
// real impls
private val a11yInteractor = AccessibilityInteractor(a11yRepo)
- private val underTest = ActivatableNotificationViewModel(a11yInteractor)
+ private val windowRootViewBlurInteractor = kosmos.windowRootViewBlurInteractor
+ private val underTest = ActivatableNotificationViewModel(a11yInteractor,
+ windowRootViewBlurInteractor)
@Test
fun isTouchable_whenA11yTouchExplorationDisabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index 4a761917..cb4642c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -18,9 +18,11 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.res.R
@@ -44,6 +46,7 @@
import org.mockito.Mockito.description
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
+import org.mockito.kotlin.doNothing
private const val VIEW_HEIGHT = 100
private const val FULL_SHADE_APPEAR_TRANSLATION = 300
@@ -53,18 +56,20 @@
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class StackStateAnimatorTest : SysuiTestCase() {
+
+ @get:Rule val setFlagsRule = SetFlagsRule()
@get:Rule val animatorTestRule = AnimatorTestRule(this)
private lateinit var stackStateAnimator: StackStateAnimator
private lateinit var headsUpAnimator: HeadsUpAnimator
private val stackScroller: NotificationStackScrollLayout = mock()
private val view: ExpandableView = mock()
- private val viewState: ExpandableViewState =
- ExpandableViewState().apply { height = VIEW_HEIGHT }
+ private lateinit var viewState: ExpandableViewState
private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor()
@Before
fun setUp() {
+ viewState = ExpandableViewState().apply { height = VIEW_HEIGHT }
overrideResource(
R.dimen.go_to_full_shade_appearing_translation,
FULL_SHADE_APPEAR_TRANSLATION,
@@ -153,6 +158,7 @@
)
}
+ @DisableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
@Test
@EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOn() {
@@ -238,6 +244,32 @@
.removeFromTransientContainer()
}
+ @EnableFlags(Flags.FLAG_PHYSICAL_NOTIFICATION_MOVEMENT)
+ @Test
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim_physical() {
+ val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
+ clearInvocations(view)
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view)
+ .performRemoveAnimation(
+ /* duration= */ eq(disappearDuration),
+ /* delay= */ eq(0L),
+ /* translationDirection= */ eq(0f),
+ /* isHeadsUpAnimation= */ eq(true),
+ /* isHeadsUpCycling= */ eq(false),
+ /* onStartedRunnable= */ any(),
+ /* onFinishedRunnable= */ runnableCaptor.capture(),
+ /* animationListener= */ any(),
+ /* clipSide= */ eq(ExpandableView.ClipSide.BOTTOM),
+ )
+
+ runnableCaptor.value.run() // execute the end runnable
+ verify(view, description("should be called at the end of the disappear animation"))
+ .removeFromTransientContainer()
+ }
+
@Test
fun initView_updatesResources() {
// Given: the resource values are initialized in the SSA
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
index ef415c9..6ee1c4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack
+import android.animation.AnimatorTestRule
import android.animation.ValueAnimator
import android.view.View
import androidx.test.annotation.UiThreadTest
@@ -28,6 +29,7 @@
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.TAG_ANIMATOR_TRANSLATION_Y
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.Y_TRANSLATION
import org.junit.Assert
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.math.log2
@@ -38,6 +40,8 @@
@UiThreadTest
class ViewStateTest : SysuiTestCase() {
private val viewState = ViewState(true /* usePhysicsForMovement */)
+ @get:Rule
+ val animatorTestRule = AnimatorTestRule(this)
@Suppress("DIVISION_BY_ZERO")
@Test
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6ff1240..43ea2c3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1024,6 +1024,12 @@
<string name="hearing_devices_ambient_control_left">Left</string>
<!-- QuickSettings: The text to show the control is for right side device. [CHAR LIMIT=30] -->
<string name="hearing_devices_ambient_control_right">Right</string>
+ <!-- QuickSettings: Content description for unified ambient control slider. [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_control_description">Surroundings</string>
+ <!-- QuickSettings: Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_control_left_description">Left surroundings</string>
+ <!-- QuickSettings: Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
+ <string name="hearing_devices_ambient_control_right_description">Right surroundings</string>
<!-- QuickSettings: Content description for a button, that expands ambient volume sliders [CHAR_LIMIT=NONE] -->
<string name="hearing_devices_ambient_expand_controls">Expand to left and right separated controls</string>
<!-- QuickSettings: Content description for a button, that collapses ambient volume sliders [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5b96e50..4431dda 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -871,6 +871,7 @@
</style>
<style name="MediaPlayer.SessionAction.Primary" parent="MediaPlayer.SessionAction">
+ <item name="android:tint">@android:color/system_on_primary_dark</item>
<item name="android:background">@drawable/qs_media_round_button_background</item>
<item name="android:backgroundTint">@color/media_player_solid_button_bg</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
index 5247acc..33c9eb1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -327,8 +327,19 @@
slider.addOnChangeListener(mSliderOnChangeListener);
if (side == SIDE_LEFT) {
slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_left));
+ slider.setContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_left));
+ slider.setSliderContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_left_description));
} else if (side == SIDE_RIGHT) {
slider.setTitle(mContext.getString(R.string.hearing_devices_ambient_control_right));
+ slider.setContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_right));
+ slider.setSliderContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_right_description));
+ } else {
+ slider.setSliderContentDescription(
+ mContext.getString(R.string.hearing_devices_ambient_control_description));
}
mSideToSliderMap.put(side, slider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
index 1a068c4..5c0ad3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeSlider.java
@@ -92,6 +92,11 @@
mSlider = requireViewById(R.id.ambient_volume_slider);
mSlider.addOnSliderTouchListener(mSliderTouchListener);
mSlider.addOnChangeListener(mSliderChangeListener);
+
+ setFocusable(false);
+ setClickable(false);
+ mSlider.setFocusable(false);
+ mSlider.setClickable(false);
}
/**
@@ -178,6 +183,13 @@
return (int) Math.ceil((value - min) / levelGap);
}
+ /** Sets the content description to the ambient volume slider. */
+ public void setSliderContentDescription(CharSequence contentDescription) {
+ if (mSlider != null) {
+ mSlider.setContentDescription(contentDescription);
+ }
+ }
+
/** Interface definition for a callback invoked when a slider's value is changed. */
public interface OnChangeListener {
/** Called when the finger is take off from the slider. */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
deleted file mode 100644
index b531d15..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2024 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.communal.ui.viewmodel
-
-import android.annotation.SuppressLint
-import android.app.DreamManager
-import android.content.Intent
-import android.provider.Settings
-import androidx.compose.runtime.getValue
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
-import com.android.systemui.communal.shared.log.CommunalUiEvent
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.not
-import com.android.systemui.util.kotlin.isDevicePluggedIn
-import com.android.systemui.util.kotlin.sample
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.receiveAsFlow
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-class CommunalToDreamButtonViewModel
-@AssistedInject
-constructor(
- @Background private val backgroundContext: CoroutineContext,
- batteryController: BatteryController,
- private val prefsInteractor: CommunalPrefsInteractor,
- private val settingsInteractor: CommunalSettingsInteractor,
- private val activityStarter: ActivityStarter,
- private val dreamManager: DreamManager,
- private val uiEventLogger: UiEventLogger,
-) : ExclusiveActivatable() {
-
- private val hydrator = Hydrator("CommunalToDreamButtonViewModel.hydrator")
- private val _requests = Channel<Unit>(Channel.BUFFERED)
-
- /** Whether we should show a button on hub to switch to dream. */
- val shouldShowDreamButtonOnHub: Boolean by
- hydrator.hydratedStateOf(
- traceName = "shouldShowDreamButtonOnHub",
- initialValue = false,
- source = batteryController.isDevicePluggedIn().distinctUntilChanged(),
- )
-
- /** Return whether to show the dream button tooltip. */
- val shouldShowTooltip: Boolean by
- hydrator.hydratedStateOf(
- traceName = "shouldShowTooltip",
- initialValue = false,
- source =
- allOf(
- not(prefsInteractor.isDreamButtonTooltipDismissed),
- prefsInteractor.isHubOnboardingDismissed,
- ),
- )
-
- /** Set the dream button tooltip to be dismissed. */
- fun setDreamButtonTooltipDismissed() {
- prefsInteractor.setDreamButtonTooltipDismissed()
- }
-
- /** Handle a tap on the "show dream" button. */
- fun onShowDreamButtonTap() {
- uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP)
- _requests.trySend(Unit)
- }
-
- @SuppressLint("MissingPermission")
- override suspend fun onActivated(): Nothing = coroutineScope {
- launch {
- _requests
- .receiveAsFlow()
- .sample(settingsInteractor.isScreensaverEnabled)
- .collectLatest { enabled ->
- withContext(backgroundContext) {
- if (enabled) {
- dreamManager.startDream()
- } else {
- activityStarter.postStartActivityDismissingKeyguard(
- Intent(Settings.ACTION_DREAM_SETTINGS),
- 0,
- )
- }
- }
- }
- }
-
- launch { hydrator.activate() }
-
- awaitCancellation()
- }
-
- @AssistedFactory
- interface Factory {
- fun create(): CommunalToDreamButtonViewModel
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 9b443f5..5d62c02 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -90,15 +90,16 @@
fun logCarouselVisible() = buffer.log(TAG, LogLevel.DEBUG, {}, { "showing carousel" })
- fun logMediaHostVisibility(location: Int, visible: Boolean) {
+ fun logMediaHostVisibility(location: Int, visible: Boolean, oldState: Boolean) {
buffer.log(
TAG,
LogLevel.DEBUG,
{
int1 = location
bool1 = visible
+ bool2 = oldState
},
- { "media host visibility changed location=$location, visible:$visible" },
+ { "media host visibility changed location=$location, visible:$visible, was:$oldState" },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index c689408..69006c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -1129,10 +1129,11 @@
traceSection("MediaHierarchyManager#updateHostAttachment") {
if (SceneContainerFlag.isEnabled) {
// No need to manage transition states - just update the desired location directly
- logger.logMediaHostAttachment(desiredLocation)
+ val host = getHost(desiredLocation)
+ logger.logMediaHostAttachment(desiredLocation, host?.visible)
mediaCarouselController.onDesiredLocationChanged(
desiredLocation = desiredLocation,
- desiredHostState = getHost(desiredLocation),
+ desiredHostState = host,
animate = false,
)
return
@@ -1169,7 +1170,8 @@
// that and directly set the mediaFrame's bounds within the premeasured host.
targetHost.addView(mediaFrame)
}
- logger.logMediaHostAttachment(currentAttachmentLocation)
+ val host = getHost(currentAttachmentLocation)
+ logger.logMediaHostAttachment(currentAttachmentLocation, host?.visible)
if (isCrossFadeAnimatorRunning) {
// When cross-fading with an animation, we only notify the media carousel of the
// location change, once the view is reattached to the new place and not
@@ -1313,6 +1315,7 @@
isHomeScreenShadeVisibleToUser() ||
isGlanceableHubVisibleToUser()
val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation
+ logger.logUserVisibilityChange(shadeVisible, mediaVisible)
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
shadeVisible && mediaVisible
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
index 1514db3..089d16b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
@@ -36,7 +36,7 @@
int1 = width
int2 = height
},
- { "size ($str1): $int1 x $int2" }
+ { "size ($str1): $int1 x $int2" },
)
}
@@ -49,11 +49,31 @@
int1 = startLocation
int2 = endLocation
},
- { "location ($str1): $int1 -> $int2" }
+ { "location ($str1): $int1 -> $int2" },
)
}
- fun logMediaHostAttachment(host: Int) {
- buffer.log(TAG, LogLevel.DEBUG, { int1 = host }, { "Host (updateHostAttachment): $int1" })
+ fun logMediaHostAttachment(host: Int, visible: Boolean?) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = host
+ str1 = visible.toString()
+ },
+ { "Host (updateHostAttachment): $int1 visible $str1" },
+ )
+ }
+
+ fun logUserVisibilityChange(shadeVisible: Boolean, mediaVisible: Boolean) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ bool1 = shadeVisible
+ bool2 = mediaVisible
+ },
+ { "User visibility shade: $shadeVisible media: $mediaVisible" },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 11251cd..a518349 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -208,6 +208,7 @@
* the visibility has changed
*/
fun updateViewVisibility() {
+ val oldState = state.visible
state.visible =
if (mediaCarouselController.isLockedAndHidden()) {
false
@@ -217,9 +218,9 @@
mediaDataManager.hasAnyMediaOrRecommendation()
}
val newVisibility = if (visible) View.VISIBLE else View.GONE
- if (newVisibility != hostView.visibility) {
+ if (oldState != state.visible || newVisibility != hostView.visibility) {
hostView.visibility = newVisibility
- debugLogger.logMediaHostVisibility(location, visible)
+ debugLogger.logMediaHostVisibility(location, visible, oldState)
visibleChangedListeners.forEach { it.invoke(visible) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 4bb12e5..6ebe024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -57,6 +57,12 @@
private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator();
private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator();
private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator();
+ private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() {
+ @Override
+ public Object extractData(ExpandableNotificationRow row) {
+ return row.getEntry().getSbn().getNotification();
+ }
+ };
private final ExpandableNotificationRow mRow;
private final ArrayList<Processor> mProcessors = new ArrayList<>();
@@ -103,26 +109,31 @@
// To hide the icons if they are the same and the color is the same
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.icon,
+ ICON_EXTRACTOR,
iconVisibilityComparator,
VISIBILITY_APPLICATOR));
// To grey out the icons when they are not the same, or they have the same color
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.status_bar_latest_event_content,
+ ICON_EXTRACTOR,
greyComparator,
greyApplicator));
// To show the large icon on the left side instead if all the small icons are the same
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.status_bar_latest_event_content,
+ ICON_EXTRACTOR,
iconVisibilityComparator,
LEFT_ICON_APPLICATOR));
// To only show the work profile icon in the group header
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.profile_badge,
+ null /* Extractor */,
BADGE_COMPARATOR,
VISIBILITY_APPLICATOR));
// To hide the app name in group children
mProcessors.add(new Processor(mRow,
com.android.internal.R.id.app_name_text,
+ null,
APP_NAME_COMPARATOR,
APP_NAME_APPLICATOR));
// To hide the header text if it's the same
@@ -242,20 +253,23 @@
private static class Processor {
private final int mId;
+ private final DataExtractor mExtractor;
private final ViewComparator mComparator;
private final ResultApplicator mApplicator;
private final ExpandableNotificationRow mParentRow;
private boolean mApply;
private View mParentView;
+ private Object mParentData;
public static Processor forTextView(ExpandableNotificationRow row, int id) {
- return new Processor(row, id, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR);
+ return new Processor(row, id, null, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR);
}
- Processor(ExpandableNotificationRow row, int id,
+ Processor(ExpandableNotificationRow row, int id, DataExtractor extractor,
ViewComparator comparator,
ResultApplicator applicator) {
mId = id;
+ mExtractor = extractor;
mApplicator = applicator;
mComparator = comparator;
mParentRow = row;
@@ -265,6 +279,7 @@
NotificationViewWrapper wrapper = mParentRow.getNotificationViewWrapper();
View header = wrapper == null ? null : wrapper.getNotificationHeader();
mParentView = header == null ? null : header.findViewById(mId);
+ mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
mApply = !mComparator.isEmpty(mParentView);
}
@@ -282,7 +297,9 @@
// when for example showing an undo notification
return;
}
- mApply = mComparator.compare(mParentView, ownView);
+ Object childData = mExtractor == null ? null : mExtractor.extractData(row);
+ mApply = mComparator.compare(mParentView, ownView,
+ mParentData, childData);
}
public void apply(ExpandableNotificationRow row) {
@@ -314,9 +331,11 @@
/**
* @param parent the view with the given id in the group header
* @param child the view with the given id in the child notification
+ * @param parentData optional data for the parent
+ * @param childData optional data for the child
* @return whether to views are the same
*/
- boolean compare(View parent, View child);
+ boolean compare(View parent, View child, Object parentData, Object childData);
boolean isEmpty(View view);
}
@@ -327,7 +346,7 @@
private static class BadgeComparator implements ViewComparator {
@Override
- public boolean compare(View parent, View child) {
+ public boolean compare(View parent, View child, Object parentData, Object childData) {
return parent.getVisibility() != View.GONE;
}
@@ -345,7 +364,7 @@
private static class TextViewComparator implements ViewComparator {
@Override
- public boolean compare(View parent, View child) {
+ public boolean compare(View parent, View child, Object parentData, Object childData) {
TextView parentView = (TextView) parent;
CharSequence parentText = parentView == null ? "" : parentView.getText();
TextView childView = (TextView) child;
@@ -361,7 +380,7 @@
private abstract static class IconComparator implements ViewComparator {
@Override
- public boolean compare(View parent, View child) {
+ public boolean compare(View parent, View child, Object parentData, Object childData) {
return false;
}
@@ -421,14 +440,14 @@
private static class AppNameComparator extends TextViewComparator {
@Override
- public boolean compare(View parent, View child) {
+ public boolean compare(View parent, View child, Object parentData, Object childData) {
if (isEmpty(child)) {
// In headerless notifications the AppName view exists but is usually GONE (and not
// populated). We need to treat this case as equal to the header in order to
// deduplicate the view.
return true;
}
- return super.compare(parent, child);
+ return super.compare(parent, child, parentData, childData);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
index 74faf25..0a24d7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PhysicsPropertyAnimator.kt
@@ -15,12 +15,12 @@
*/
package com.android.systemui.statusbar.notification
+import android.util.FloatProperty
import android.util.Property
import android.view.View
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.SpringAnimation
-import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.dynamicanimation.animation.DynamicAnimation
+import com.android.internal.dynamicanimation.animation.SpringAnimation
+import com.android.internal.dynamicanimation.animation.SpringForce
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator.Companion.createDefaultSpring
import com.android.systemui.statusbar.notification.stack.AnimationProperties
@@ -33,8 +33,8 @@
*/
data class PhysicsProperty(val tag: Int, val property: Property<View, Float>) {
val offsetProperty =
- object : FloatPropertyCompat<View>(property.name) {
- override fun getValue(view: View): Float {
+ object : FloatProperty<View>(property.name) {
+ override fun get(view: View): Float {
return property.get(view)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 248b528..28923b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.app.NotificationChannel.SYSTEM_RESERVED_IDS
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -89,6 +90,7 @@
object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
override fun isInSection(entry: PipelineEntry): Boolean {
return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
}
}
@@ -96,10 +98,12 @@
val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
override fun isInSection(entry: PipelineEntry): Boolean {
if (SortBySectionTimeFlag.isEnabled) {
- return highPriorityProvider.isHighPriorityConversation(entry)
- || isConversation(entry)
+ return (highPriorityProvider.isHighPriorityConversation(entry)
+ || isConversation(entry))
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
} else {
return highPriorityProvider.isHighPriorityConversation(entry)
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
}
}
@@ -111,11 +115,12 @@
}
val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) {
- // Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting.
- // All remaining conversations must be silent.
+ // Because the peopleAlertingSectioner is above this one, it will claim all conversations
+ // that are alerting. All remaining conversations must be silent.
override fun isInSection(entry: PipelineEntry): Boolean {
SortBySectionTimeFlag.assertInLegacyMode()
return isConversation(entry)
+ && entry.representativeEntry?.channel?.id !in SYSTEM_RESERVED_IDS
}
override fun getComparator(): NotifComparator {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 7959e99..2a01a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -20,6 +20,7 @@
import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
import android.graphics.PorterDuff
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
@@ -80,9 +81,13 @@
val content = viewModel.content ?: return
val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible
- key(content.identity) {
- val layoutResource = content.layoutResource ?: return
+ val layoutResource = content.layoutResource
+ if (layoutResource == null) {
+ Log.w(TAG, "not displaying promoted notif with ineligible style on AOD")
+ return
+ }
+ key(content.identity) {
val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
val sidePaddingValues = PaddingValues(horizontal = sidePaddings, vertical = 0.dp)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index a081ad5..6837cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -119,6 +119,7 @@
protected Point mTargetPoint;
private boolean mDismissed;
private boolean mRefocusOnDismiss;
+ protected boolean mIsBlurSupported;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -128,12 +129,13 @@
}
private void updateColors() {
- if (notificationRowTransparency()) {
+ if (usesTransparentBackground()) {
mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
} else {
mNormalColor = mContext.getColor(
com.android.internal.R.color.materialColorSurfaceContainerHigh);
}
+ setBackgroundToNormalColor();
mTintedRippleColor = mContext.getColor(
R.color.notification_ripple_tinted_color);
mNormalRippleColor = mContext.getColor(
@@ -144,6 +146,12 @@
mOverrideAmount = 0.0f;
}
+ private void setBackgroundToNormalColor() {
+ if (mBackgroundNormal != null) {
+ mBackgroundNormal.setNormalColor(mNormalColor);
+ }
+ }
+
/**
* Reload background colors from resources and invalidate views.
*/
@@ -173,6 +181,7 @@
mBackgroundNormal = findViewById(R.id.backgroundNormal);
mFakeShadow = findViewById(R.id.fake_shadow);
mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
+ setBackgroundToNormalColor();
initBackground();
updateBackgroundTint();
updateOutlineAlpha();
@@ -326,6 +335,21 @@
mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
}
+ public void setIsBlurSupported(boolean isBlurSupported) {
+ if (!notificationRowTransparency()) {
+ return;
+ }
+ boolean usedTransparentBackground = usesTransparentBackground();
+ mIsBlurSupported = isBlurSupported;
+ if (usedTransparentBackground != usesTransparentBackground()) {
+ updateBackgroundColors();
+ }
+ }
+
+ protected boolean usesTransparentBackground() {
+ return mIsBlurSupported && notificationRowTransparency();
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 179951f..3ef1fd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -20,8 +20,8 @@
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static com.android.systemui.Flags.notificationsPinnedHunInShade;
import static com.android.systemui.Flags.notificationRowTransparency;
+import static com.android.systemui.Flags.notificationsPinnedHunInShade;
import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
@@ -1678,14 +1678,15 @@
if (view != null) {
view.setBackgroundTintColor(color);
}
- if (notificationRowTransparency()
- && (mBackgroundNormal != null)) {
+ if (notificationRowTransparency() && mBackgroundNormal != null) {
if (NotificationBundleUi.isEnabled()) {
- mBackgroundNormal.setBgIsColorized(mEntryAdapter.isColorized());
+ mBackgroundNormal.setBgIsColorized(
+ usesTransparentBackground() && mEntryAdapter.isColorized());
} else {
if (mEntry != null) {
mBackgroundNormal.setBgIsColorized(
- mEntry.getSbn().getNotification().isColorized());
+ usesTransparentBackground()
+ && mEntry.getSbn().getNotification().isColorized());
}
}
}
@@ -3021,6 +3022,7 @@
mUserLocked = userLocked;
mPrivateLayout.setUserExpanding(userLocked);
+ mPublicLayout.setUserExpanding(userLocked);
// This is intentionally not guarded with mIsSummaryWithChildren since we might have had
// children but not anymore.
if (mChildrenContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4978fa4..e1219e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -73,7 +73,7 @@
private int mDrawableAlpha = 255;
private final ColorStateList mLightColoredStatefulColors;
private final ColorStateList mDarkColoredStatefulColors;
- private final int mNormalColor;
+ private int mNormalColor;
private boolean mBgIsColorized = false;
private boolean mForceOpaque = false;
private final int convexR = 9;
@@ -89,15 +89,13 @@
R.color.notification_state_color_light);
mDarkColoredStatefulColors = getResources().getColorStateList(
R.color.notification_state_color_dark);
- if (notificationRowTransparency()) {
- mNormalColor = SurfaceEffectColors.surfaceEffect1(getContext());
- } else {
- mNormalColor = mContext.getColor(
- com.android.internal.R.color.materialColorSurfaceContainerHigh);
- }
mFocusOverlayStroke = getResources().getDimension(R.dimen.notification_focus_stroke_width);
}
+ public void setNormalColor(int color) {
+ mNormalColor = color;
+ }
+
@Override
public void onTargetVisibilityChanged(boolean targetVisible) {
if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
index f0b5c36..8984f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt
@@ -50,6 +50,15 @@
view.registerListenersWhileAttached(touchHandler)
}
}
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.isBlurSupported.collect { supported ->
+ view.setIsBlurSupported(supported)
+ }
+ }
+ }
+ }
}
private suspend fun ActivatableNotificationView.registerListenersWhileAttached(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
index f46d424..9a86ffb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row.ui.viewmodel
import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.flow.Flow
@@ -26,25 +27,34 @@
interface ActivatableNotificationViewModel : ExpandableOutlineViewModel {
/** Does the view react to touches? */
val isTouchable: Flow<Boolean>
+ val isBlurSupported: Flow<Boolean>
companion object {
operator fun invoke(
a11yInteractor: AccessibilityInteractor,
- ): ActivatableNotificationViewModel = ActivatableNotificationViewModelImpl(a11yInteractor)
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ ): ActivatableNotificationViewModel =
+ ActivatableNotificationViewModelImpl(a11yInteractor, windowRootViewBlurInteractor)
}
}
private class ActivatableNotificationViewModelImpl(
a11yInteractor: AccessibilityInteractor,
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
) : ActivatableNotificationViewModel {
override val isTouchable: Flow<Boolean> =
// If a11y touch exploration is enabled, then the activatable view should ignore touches
a11yInteractor.isTouchExplorationEnabled.map { !it }
+ override val isBlurSupported: Flow<Boolean> =
+ windowRootViewBlurInteractor.isBlurCurrentlySupported
}
@Module
object ActivatableNotificationViewModelModule {
@Provides
- fun provideViewModel(interactor: AccessibilityInteractor) =
- ActivatableNotificationViewModel(interactor)
+ fun provideViewModel(
+ a11yInteractor: AccessibilityInteractor,
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor
+ ) =
+ ActivatableNotificationViewModel(a11yInteractor, windowRootViewBlurInteractor)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
index 3d60092..4d01cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AnimationProperties.java
@@ -23,7 +23,7 @@
import android.view.View;
import android.view.animation.Interpolator;
-import androidx.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 9a5cf9c..19abfa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -32,10 +32,9 @@
import android.util.Property;
import android.view.View;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 2ef6f36..29dbeb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -30,10 +30,9 @@
import android.view.View;
import android.view.animation.Interpolator;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
-
import com.android.app.animation.Interpolators;
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AnimatableProperty;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
deleted file mode 100644
index 43d3eb7..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2024 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.communal.ui.viewmodel
-
-import android.service.dream.dreamManager
-import com.android.internal.logging.uiEventLogger
-import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.plugins.activityStarter
-import com.android.systemui.statusbar.policy.batteryController
-
-val Kosmos.communalToDreamButtonViewModel by
- Kosmos.Fixture {
- CommunalToDreamButtonViewModel(
- backgroundContext = testDispatcher,
- batteryController = batteryController,
- prefsInteractor = communalPrefsInteractor,
- settingsInteractor = communalSettingsInteractor,
- activityStarter = activityStarter,
- dreamManager = dreamManager,
- uiEventLogger = uiEventLogger,
- )
- }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
index 2523975..7ccbdb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelKosmos.kt
@@ -19,9 +19,11 @@
import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
val Kosmos.activatableNotificationViewModel by Fixture {
ActivatableNotificationViewModel.invoke(
a11yInteractor = accessibilityInteractor,
+ windowRootViewBlurInteractor = windowRootViewBlurInteractor,
)
}
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 735635c..b41ce0f 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -268,18 +268,45 @@
}
/**
+ * Write bytecode to pop the 2 uninitialized instances out of the stack
+ * after performing constructor redirection.
+ */
+fun adjustStackForConstructorRedirection(writer: MethodVisitor) {
+ // Stack: { uninitialized, uninitialized, obj }
+ writer.visitInsn(Opcodes.SWAP)
+ // Stack: { uninitialized, obj, uninitialized }
+ writer.visitInsn(Opcodes.POP)
+ // Stack: { uninitialized, obj }
+ writer.visitInsn(Opcodes.SWAP)
+ // Stack: { obj, uninitialized }
+ writer.visitInsn(Opcodes.POP)
+ // Stack: { obj }
+
+ // We end up with only the desired object on the stack
+}
+
+/**
* Given a method descriptor, insert an [argType] as the first argument to it.
*/
fun prependArgTypeToMethodDescriptor(methodDescriptor: String, classInternalName: String): String {
val returnType = Type.getReturnType(methodDescriptor)
val argTypes = Type.getArgumentTypes(methodDescriptor).toMutableList()
- argTypes.add(0, Type.getType("L" + classInternalName + ";"))
+ argTypes.add(0, Type.getType("L$classInternalName;"))
return Type.getMethodDescriptor(returnType, *argTypes.toTypedArray())
}
/**
+ * Given a method descriptor, change the return type to [classInternalName].
+ */
+fun changeMethodDescriptorReturnType(methodDescriptor: String, classInternalName: String): String {
+ val argTypes = Type.getArgumentTypes(methodDescriptor)
+ val returnType = Type.getType("L$classInternalName;")
+ return Type.getMethodDescriptor(returnType, *argTypes)
+}
+
+/**
* Return the "visibility" modifier from an `access` integer.
*
* (see https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1)
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index d0c97c0..dd353e9 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -100,7 +100,6 @@
methodName: String,
methodDesc: String,
replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
- policy: FilterPolicyWithReason,
)
}
@@ -286,9 +285,10 @@
methodName: String,
methodDesc: String,
replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
- policy: FilterPolicyWithReason,
) {
- imf.setPolicyForMethod(className, methodName, methodDesc, policy)
+ // Keep the source method, because the target method may call it.
+ imf.setPolicyForMethod(className, methodName, methodDesc,
+ FilterPolicy.Keep.withReason(FILTER_REASON))
methodReplaceSpec.add(replaceSpec)
}
}
@@ -642,7 +642,6 @@
methodName,
signature,
spec,
- policyWithReason,
)
} else {
// It's an in-class replace.
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 70e7d46..b8a3576 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -17,7 +17,10 @@
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.CTOR_NAME
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.adjustStackForConstructorRedirection
+import com.android.hoststubgen.asm.changeMethodDescriptorReturnType
import com.android.hoststubgen.asm.prependArgTypeToMethodDescriptor
import com.android.hoststubgen.asm.writeByteCodeToPushArguments
import com.android.hoststubgen.asm.writeByteCodeToReturn
@@ -33,6 +36,7 @@
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.INVOKEINTERFACE
+import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.INVOKESTATIC
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.Type
@@ -376,53 +380,90 @@
val callerMethodName: String,
next: MethodVisitor?,
) : MethodVisitor(OPCODE_VERSION, next) {
- override fun visitMethodInsn(
+
+ private fun doReplace(
opcode: Int,
- owner: String?,
- name: String?,
- descriptor: String?,
- isInterface: Boolean,
- ) {
+ owner: String,
+ name: String,
+ descriptor: String,
+ ): Boolean {
when (opcode) {
INVOKESTATIC, INVOKEVIRTUAL, INVOKEINTERFACE -> {}
- else -> {
- // Don't touch other opcodes.
- super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
- return
- }
+ // We only support INVOKESPECIAL when replacing constructors.
+ INVOKESPECIAL -> if (name != CTOR_NAME) return false
+ // Don't touch other opcodes.
+ else -> return false
}
+
val to = filter.getMethodCallReplaceTo(
- currentClassName, callerMethodName, owner!!, name!!, descriptor!!
+ currentClassName, callerMethodName, owner, name, descriptor
)
if (to == null
// Don't replace if the target is the callsite.
|| (to.className == currentClassName && to.methodName == callerMethodName)
) {
- super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
- return
+ return false
}
- // Replace the method call with a (static) call to the target method.
- // If it's a non-static call, the target method's first argument will receive "this".
- // (Because of that, we don't need to manipulate the stack. Just replace the
- // method call.)
+ if (opcode != INVOKESPECIAL) {
+ // It's either a static method call or virtual method call.
+ // Either way, we don't manipulate the stack and send the original arguments
+ // as is to the target method.
+ //
+ // If the call is a virtual call (INVOKEVIRTUAL or INVOKEINTERFACE), then
+ // the first argument in the stack is the "this" object, so the target
+ // method must have an extra argument as the first argument to receive it.
+ // We update the method descriptor with prependArgTypeToMethodDescriptor()
+ // to absorb this difference.
- val toDesc = if (opcode == INVOKESTATIC) {
- // Static call to static call, no need to change the desc.
- descriptor
+ val toDesc = if (opcode == INVOKESTATIC) {
+ descriptor
+ } else {
+ prependArgTypeToMethodDescriptor(descriptor, owner)
+ }
+
+ mv.visitMethodInsn(
+ INVOKESTATIC,
+ to.className,
+ to.methodName,
+ toDesc,
+ false
+ )
} else {
- // Need to prepend the "this" type to the descriptor.
- prependArgTypeToMethodDescriptor(descriptor, owner)
+ // Because an object initializer does not return a value, the newly created
+ // but uninitialized object will be dup-ed at the bottom of the stack.
+ // We first call the target method to consume the constructor arguments at the top.
+
+ val toDesc = changeMethodDescriptorReturnType(descriptor, owner)
+
+ // Before stack: { uninitialized, uninitialized, args... }
+ mv.visitMethodInsn(
+ INVOKESTATIC,
+ to.className,
+ to.methodName,
+ toDesc,
+ false
+ )
+ // After stack: { uninitialized, uninitialized, obj }
+
+ // Next we pop the 2 uninitialized instances out of the stack.
+ adjustStackForConstructorRedirection(mv)
}
- mv.visitMethodInsn(
- INVOKESTATIC,
- to.className,
- to.methodName,
- toDesc,
- false
- )
+ return true
+ }
+
+ override fun visitMethodInsn(
+ opcode: Int,
+ owner: String,
+ name: String,
+ descriptor: String,
+ isInterface: Boolean,
+ ) {
+ if (!doReplace(opcode, owner, name, descriptor)) {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+ }
}
}
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
index e846d6e..2b942a9 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -1883,6 +1883,42 @@
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 65
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -1891,7 +1927,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -1937,10 +1973,28 @@
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
}
SourceFile: "TinyFrameworkMethodCallReplace.java"
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -1950,7 +2004,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 5
+ interfaces: 0, fields: 0, methods: 6, attributes: 5
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2008,6 +2062,21 @@
x: ireturn
LineNumberTable:
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 i I
+
private static int originalAdd(int, int);
descriptor: (II)I
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -2046,6 +2115,7 @@
android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
BootstrapMethods:
x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
@@ -2053,8 +2123,9 @@
#x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
#x ()V
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
index be95fe0..d493ad1 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -2002,6 +2002,51 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 65
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2010,7 +2055,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
+ interfaces: 0, fields: 0, methods: 4, attributes: 4
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -2065,9 +2110,30 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -2081,7 +2147,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 6
+ interfaces: 0, fields: 0, methods: 6, attributes: 6
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2148,6 +2214,48 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 a I
+ 0 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
@@ -2167,6 +2275,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2184,6 +2293,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index 667981f..8978a7a 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -2413,6 +2413,66 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 65
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 11 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2421,7 +2481,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 4
+ interfaces: 0, fields: 0, methods: 5, attributes: 4
Constant pool:
{
private static {};
@@ -2501,8 +2561,34 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ x: ldc #x // String newConstructorTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
@@ -2517,7 +2603,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 6
+ interfaces: 0, fields: 0, methods: 7, attributes: 6
Constant pool:
{
private static {};
@@ -2609,18 +2695,70 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String constructorReplaceTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String originalAdd
+ x: ldc #x // String (II)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 a I
+ 11 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
- x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+ x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
+ x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: aload_0
- x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+ x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z
x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
x: return
@@ -2633,6 +2771,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2650,6 +2789,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 8f56f0e..406c611 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -1883,6 +1883,42 @@
InnerClasses:
public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -1891,7 +1927,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 3
+ interfaces: 0, fields: 0, methods: 4, attributes: 3
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -1937,10 +1973,28 @@
Start Length Slot Name Signature
0 4 0 a I
0 4 1 b I
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
}
SourceFile: "TinyFrameworkMethodCallReplace.java"
NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
Compiled from "TinyFrameworkMethodCallReplace.java"
@@ -1950,7 +2004,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 5
+ interfaces: 0, fields: 0, methods: 6, attributes: 5
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2008,6 +2062,21 @@
x: ireturn
LineNumberTable:
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 9 0 i I
+
private static int originalAdd(int, int);
descriptor: (II)I
flags: (0x000a) ACC_PRIVATE, ACC_STATIC
@@ -2046,6 +2115,7 @@
android.hosttest.annotation.HostSideTestWholeClassKeep
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
BootstrapMethods:
x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
@@ -2053,8 +2123,9 @@
#x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
#x ()V
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+ public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
index c918bf8..6a8e488 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -2002,6 +2002,51 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 1, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 0 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2010,7 +2055,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 3, attributes: 4
+ interfaces: 0, fields: 0, methods: 4, attributes: 4
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
@@ -2065,9 +2110,30 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
- public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
x: #x()
@@ -2081,7 +2147,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 6
+ interfaces: 0, fields: 0, methods: 6, attributes: 6
Constant pool:
{
public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
@@ -2148,6 +2214,48 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=2, args_size=2
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 6 0 a I
+ 0 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
@@ -2167,6 +2275,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2184,6 +2293,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index 28065bf..d8e7632 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -2413,6 +2413,66 @@
#x ()Ljava/lang/Integer;
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester.class
+ Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+Constant pool:
+{
+ public int i;
+ descriptor: I
+ flags: (0x0001) ACC_PUBLIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester(int);
+ descriptor: (I)V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: ldc #x // String <init>
+ x: ldc #x // String (I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: aload_0
+ x: iload_1
+ x: putfield #x // Field i:I
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ 11 10 1 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
Compiled from "TinyFrameworkMethodCallReplace.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
@@ -2421,7 +2481,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 4, attributes: 4
+ interfaces: 0, fields: 0, methods: 5, attributes: 4
Constant pool:
{
private static {};
@@ -2501,8 +2561,34 @@
RuntimeVisibleAnnotations:
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester newConstructorTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ x: ldc #x // String newConstructorTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: iconst_1
+ x: iadd
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester."<init>":(I)V
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 11 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
SourceFile: "TinyFrameworkMethodCallReplace.java"
RuntimeVisibleAnnotations:
@@ -2517,7 +2603,7 @@
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
super_class: #x // java/lang/Object
- interfaces: 0, fields: 0, methods: 5, attributes: 6
+ interfaces: 0, fields: 0, methods: 7, attributes: 6
Constant pool:
{
private static {};
@@ -2609,18 +2695,70 @@
x: #x()
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester constructorReplaceTester(int);
+ descriptor: (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String constructorReplaceTester
+ x: ldc #x // String (I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
+ x: dup
+ x: iload_0
+ x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester:(I)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester;
+ x: swap
+ x: pop
+ x: swap
+ x: pop
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 13 0 i I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+ private static int originalAdd(int, int);
+ descriptor: (II)I
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=4, locals=2, args_size=2
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+ x: ldc #x // String originalAdd
+ x: ldc #x // String (II)I
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iload_0
+ x: iload_1
+ x: iadd
+ x: iconst_1
+ x: isub
+ x: ireturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 6 0 a I
+ 11 6 1 b I
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
- x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
- x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+ x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0
+ x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
x: aload_0
- x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+ x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z
x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
x: return
@@ -2633,6 +2771,7 @@
com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
}
InnerClasses:
+ public static #x= #x of #x; // ConstructorTester=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
SourceFile: "TinyFrameworkMethodCallReplace.java"
@@ -2650,6 +2789,7 @@
#x ()V
NestMembers:
com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+ com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ConstructorTester
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
Compiled from "TinyFrameworkNative.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 1f64a3c..cbaad2e 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -68,6 +68,10 @@
class java.lang.Thread keep
method start ()V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.startThread
+# Used to test constructor replacement.
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ConstructorTester keepclass
+ method <init> (I)V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.newConstructorTester
+
# "rename" takes a type internal name, so '/'s is used as a separator.
# The leading / in the prefix is not needed (it'll be stripped), but it's added to make
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
index 57c69a3..d850be8 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
@@ -41,10 +41,23 @@
return originalAdd(1, 2);
}
+ public static ConstructorTester constructorReplaceTester(int i) {
+ // This object construction will be replaced with ReplaceTo.newConstructorTester().
+ return new ConstructorTester(i);
+ }
+
private static int originalAdd(int a, int b) {
return a + b - 1; // Original is broken.
}
+ public static class ConstructorTester {
+ public int i;
+
+ public ConstructorTester(int i) {
+ this.i = i;
+ }
+ }
+
public static class ReplaceTo {
public static void startThread(Thread thread) {
thread.setDaemon(true);
@@ -54,5 +67,9 @@
public static int add(int a, int b) {
return a + b;
}
+
+ public static ConstructorTester newConstructorTester(int i) {
+ return new ConstructorTester(i + 1);
+ }
}
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index b8d6be4..89fcd30 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -323,4 +323,10 @@
assertThat(TinyFrameworkMethodCallReplace.staticMethodCallReplaceTester())
.isEqualTo(3);
}
+
+ @Test
+ public void testConstructorCallReplace() throws Exception {
+ assertThat(TinyFrameworkMethodCallReplace.constructorReplaceTester(5).i)
+ .isEqualTo(6);
+ }
}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
index b6089ea..a7f481a 100644
--- a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
@@ -449,7 +449,6 @@
methodName: String,
methodDesc: String,
replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
- policy: FilterPolicyWithReason
) {
// This can't be converted to an annotation.
classHasMember = true
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4187ee2..5395d2a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2625,6 +2625,13 @@
}
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
r.foregroundNoti = notification;
+ if (r.isForeground && foregroundServiceType != previousFgsType) {
+ // An already foreground service is being started with a different fgs type
+ // which results in the type changing without typical startForeground
+ // logging.
+ Slog.w(TAG_SERVICE, "FGS type change for " + r.shortInstanceName
+ + " from " + previousFgsType + " to " + foregroundServiceType);
+ }
mAm.mProcessStateController.setForegroundServiceType(r, foregroundServiceType);
if (!r.isForeground) {
final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -9191,7 +9198,9 @@
} else {
synchronized (mAm.mPidsSelfLocked) {
callerApp = mAm.mPidsSelfLocked.get(callingPid);
- caller = callerApp.getThread();
+ if (callerApp != null) {
+ caller = callerApp.getThread();
+ }
}
}
if (callerApp == null) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c8b8909..f7d7ed5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3006,7 +3006,7 @@
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null) {
int rawUidMode = mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), code);
+ uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, code), code);
if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
return raw ? rawUidMode :
@@ -3069,7 +3069,7 @@
int switchCode = AppOpsManager.opToSwitch(code);
int rawUidMode = mAppOpsCheckingService.getUidMode(uid,
- getPersistentId(virtualDeviceId), switchCode);
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode);
if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) {
return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode);
@@ -3396,7 +3396,7 @@
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
- getPersistentId(virtualDeviceId));
+ getPersistentDeviceIdForOp(virtualDeviceId, code));
if (attributedOp.isRunning()) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
+ code + " startTime of in progress event="
@@ -3418,15 +3418,15 @@
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- } else if (mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+ } else if (mAppOpsCheckingService.getUidMode(uidState.uid,
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
code,
mAppOpsCheckingService.getUidMode(
uidState.uid,
- getPersistentId(virtualDeviceId),
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
@@ -3478,7 +3478,8 @@
virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);
attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
- getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
+ getPersistentDeviceIdForOp(proxyVirtualDeviceId, code), uidState.getState(),
+ flags, notedCount);
if (shouldCollectAsyncNotedOp) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
@@ -4045,7 +4046,7 @@
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
- getPersistentId(virtualDeviceId));
+ getPersistentDeviceIdForOp(virtualDeviceId, code));
final UidState uidState = ops.uidState;
isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag,
virtualDeviceId, pvr.bypass, false);
@@ -4058,8 +4059,9 @@
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
} else if ((rawUidMode =
- mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode))
+ mAppOpsCheckingService.getUidMode(
+ uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
+ switchCode))
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode = uidState.evalMode(code, rawUidMode);
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
@@ -4107,11 +4109,13 @@
try {
if (isRestricted) {
attributedOp.createPaused(clientId, virtualDeviceId, proxyUid, proxyPackageName,
- proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
+ proxyAttributionTag,
+ getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
uidState.getState(), flags, attributionFlags, attributionChainId);
} else {
attributedOp.started(clientId, virtualDeviceId, proxyUid, proxyPackageName,
- proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
+ proxyAttributionTag,
+ getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
uidState.getState(), flags, attributionFlags, attributionChainId);
startType = START_TYPE_STARTED;
}
@@ -4179,15 +4183,15 @@
final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default mode per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
- if (mAppOpsCheckingService.getUidMode(
- uidState.uid, getPersistentId(virtualDeviceId), switchCode)
+ if (mAppOpsCheckingService.getUidMode(uidState.uid,
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
!= AppOpsManager.opToDefaultMode(switchCode)) {
final int uidMode =
uidState.evalMode(
code,
mAppOpsCheckingService.getUidMode(
uidState.uid,
- getPersistentId(virtualDeviceId),
+ getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
@@ -4350,7 +4354,8 @@
return;
}
final AttributedOp attributedOp =
- op.mDeviceAttributedOps.getOrDefault(getPersistentId(virtualDeviceId),
+ op.mDeviceAttributedOps.getOrDefault(
+ getPersistentDeviceIdForOp(virtualDeviceId, code),
new ArrayMap<>()).get(attributionTag);
if (attributedOp == null) {
Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
@@ -4641,7 +4646,8 @@
return true;
}
if (mVirtualDeviceManagerInternal == null) {
- return true;
+ Slog.w(TAG, "VirtualDeviceManagerInternal is null when device Id is non-default");
+ return false;
}
if (mVirtualDeviceManagerInternal.isValidVirtualDeviceId(virtualDeviceId)) {
mKnownDeviceIds.put(virtualDeviceId,
@@ -7310,7 +7316,13 @@
return packageNames;
}
- @NonNull private String getPersistentId(int virtualDeviceId) {
+ // For ops associated with device aware permissions, if virtual device id is non-default, we
+ // will return string version of that id provided the virtual device has corresponding camera or
+ // audio policy.
+ @NonNull private String getPersistentDeviceIdForOp(int virtualDeviceId, int op) {
+ virtualDeviceId = PermissionManager.resolveDeviceIdForPermissionCheck(mContext,
+ virtualDeviceId, AppOpsManager.opToPermission(op));
+
if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
return PERSISTENT_DEVICE_ID_DEFAULT;
}
@@ -7319,6 +7331,7 @@
}
String persistentId =
mVirtualDeviceManagerInternal.getPersistentIdForDevice(virtualDeviceId);
+
if (persistentId == null) {
persistentId = mKnownDeviceIds.get(virtualDeviceId);
}
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
index bbc13cc..2751835 100644
--- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -23,8 +23,6 @@
import android.view.DisplayAddress;
import android.view.DisplayEventReceiver;
-import com.android.internal.annotations.KeepForWeakReference;
-
import java.util.HashSet;
import java.util.Set;
@@ -35,7 +33,6 @@
private final DisplayModeDirector.Injector mInjector;
@SuppressWarnings("unused")
- @KeepForWeakReference
private DisplayEventReceiver mModeChangeListener;
private final SparseArray<Set<Integer>> mRejectedModesByDisplay = new SparseArray<>();
private Looper mLooper;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index f96846c..acdc79f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1067,6 +1067,9 @@
void doPostDexopt(List<ReconciledPackage> reconciledPackages,
List<InstallRequest> requests, Map<String, Boolean> createdAppId,
MoveInfo moveInfo, long acquireTime) {
+ for (InstallRequest request : requests) {
+ request.onWaitDexoptFinished();
+ }
boolean success = false;
try {
if (commitInstallPackages(reconciledPackages)) {
@@ -1218,6 +1221,7 @@
CompletableFuture<Void> future =
DexOptHelper.performDexoptIfNeededAsync(request, mDexManager);
completableFutures.add(future);
+ request.onWaitDexoptStarted();
}
if (!completableFutures.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index fbf5db5..7349204 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -1028,6 +1028,18 @@
}
}
+ public void onWaitDexoptStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_WAIT_DEXOPT);
+ }
+ }
+
+ public void onWaitDexoptFinished() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_WAIT_DEXOPT);
+ }
+ }
+
public void onDexoptFinished(DexoptResult dexoptResult) {
// Only report external profile warnings when installing from adb. The goal is to warn app
// developers if they have provided bad external profiles, so it's not beneficial to report
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 994ee42..22da3b2 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -73,6 +73,7 @@
public static final int STEP_DEXOPT = 5;
public static final int STEP_FREEZE_INSTALL = 6;
public static final int STEP_RESTORE = 7;
+ public static final int STEP_WAIT_DEXOPT = 8;
@IntDef(prefix = {"STEP_"}, value = {
STEP_PREPARE,
@@ -81,7 +82,8 @@
STEP_COMMIT,
STEP_DEXOPT,
STEP_FREEZE_INSTALL,
- STEP_RESTORE
+ STEP_RESTORE,
+ STEP_WAIT_DEXOPT
})
@Retention(RetentionPolicy.SOURCE)
public @interface StepInt {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 12f5534..58534b9 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -712,13 +712,17 @@
if (!isMagnifierActivated) {
return;
}
- // All opening/closing situations.
+ // All opening/closing/recents transitions
+ boolean notify = (flags & TRANSIT_FLAG_IS_RECENTS) != 0;
switch (type) {
case WindowManager.TRANSIT_OPEN:
case WindowManager.TRANSIT_TO_FRONT:
case WindowManager.TRANSIT_CLOSE:
case WindowManager.TRANSIT_TO_BACK:
- mUserContextChangedNotifier.onWMTransition(type, flags);
+ notify = true;
+ }
+ if (notify) {
+ mUserContextChangedNotifier.onWMTransition(type, flags);
}
}
@@ -1088,8 +1092,7 @@
// causing the notifying, or the recents/home window is removed, then we won't need the
// delayed notification anymore.
void onWMTransition(@TransitionType int type, @TransitionFlags int flags) {
- if (type == WindowManager.TRANSIT_TO_FRONT
- && (flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
+ if ((flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
// Delay the recents to front transition notification then send after if needed.
mHasDelayedNotificationForRecentsToFrontTransition = true;
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 247264f..bdde5fe 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -58,6 +58,7 @@
import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
import static android.security.Flags.preventIntentRedirectShowToast;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_AVOID_MOVE_TO_FRONT;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -1841,6 +1842,9 @@
// no-user-leaving implies not entering PiP.
transition.setCanPipOnFinish(false /* canPipOnFinish */);
}
+ if (avoidMoveToFront() && transition != null) {
+ transition.addFlag(TRANSIT_FLAG_AVOID_MOVE_TO_FRONT);
+ }
if (isIndependentLaunch && transition != null) {
transitionController.requestStartTransition(transition,
mTargetTask == null ? started.getTask() : mTargetTask,
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index d291d99..98e453b 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -25,6 +25,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.os.Build;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
@@ -66,64 +67,80 @@
/**
* Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
* @param transaction A sequence of client transaction items.
- * @throws RemoteException
- *
+ * @return {@code false} if the transaction failed because of {@link RemoteException}.
* @see ClientTransaction
*/
@VisibleForTesting
- void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
- final IApplicationThread client = transaction.getClient();
- try {
- transaction.schedule();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver transaction for " + client
- + "\ntransaction=" + transaction);
- throw e;
+ boolean scheduleTransaction(@NonNull ClientTransaction transaction) {
+ final RemoteException e = transaction.schedule();
+ if (e != null) {
+ final WindowProcessController wpc = mWms.mAtmService.getProcessController(
+ transaction.getClient());
+ Slog.w(TAG, "Failed to deliver transaction for " + wpc + "\ntransaction=" + this, e);
+ return false;
}
+ return true;
}
/**
* Similar to {@link #scheduleTransactionItem}, but it sends the transaction immediately and
* it can be called without WM lock.
*
+ * @return {@code false} if the transaction failed because of {@link RemoteException}.
* @see WindowProcessController#setReportedProcState(int)
*/
- void scheduleTransactionItemNow(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItemNow(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
final ClientTransaction clientTransaction = new ClientTransaction(client);
clientTransaction.addTransactionItem(transactionItem);
- scheduleTransaction(clientTransaction);
+ final boolean res = scheduleTransaction(clientTransaction);
+ if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+ && !res) {
+ throw new DeadObjectException();
+ }
+ return res;
}
/**
- * Schedules a transaction with the given item, delivery to client application.
+ * Schedules a transaction with the given item, delivery to client application, which may be
+ * queued to dispatched later.
*
- * @throws RemoteException
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}. If the transaction is queued, any failure will be ignored.
* @see ClientTransactionItem
*/
- void scheduleTransactionItem(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItem(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem item) throws RemoteException {
// Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
// transactions at once.
final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
clientTransaction.addTransactionItem(item);
- onClientTransactionItemScheduled(clientTransaction, false /* shouldDispatchImmediately */);
+ final boolean res = onClientTransactionItemScheduled(clientTransaction,
+ false /* shouldDispatchImmediately */);
+ if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+ && !res) {
+ throw new DeadObjectException();
+ }
+ return res;
}
/**
- * Schedules a transaction with the given items, delivery to client application.
+ * Schedules a transaction with the given items, delivery to client application, which may be
+ * queued to dispatched later.
*
- * @throws RemoteException
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}. If the transaction is queued, any failure will be ignored.
* @see ClientTransactionItem
*/
- void scheduleTransactionItems(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItems(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem... items) throws RemoteException {
- scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items);
+ return scheduleTransactionItems(client, false /* shouldDispatchImmediately */, items);
}
/**
- * Schedules a transaction with the given items, delivery to client application.
+ * Schedules a transaction with the given items, delivery to client application, which may be
+ * queued to dispatched later.
*
* @param shouldDispatchImmediately whether or not to dispatch the transaction immediately. This
* should only be {@code true} when it is important to know the
@@ -131,11 +148,11 @@
* launches an app, the server needs to know if the transaction
* is dispatched successfully, and may restart the process if
* not.
- *
- * @throws RemoteException
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}. If the transaction is queued, any failure will be ignored.
* @see ClientTransactionItem
*/
- void scheduleTransactionItems(@NonNull IApplicationThread client,
+ boolean scheduleTransactionItems(@NonNull IApplicationThread client,
boolean shouldDispatchImmediately,
@NonNull ClientTransactionItem... items) throws RemoteException {
// Wait until RootWindowContainer#performSurfacePlacementNoTrace to dispatch all pending
@@ -147,7 +164,13 @@
clientTransaction.addTransactionItem(items[i]);
}
- onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
+ final boolean res = onClientTransactionItemScheduled(clientTransaction,
+ shouldDispatchImmediately);
+ if (!com.android.window.flags.Flags.cleanupDispatchPendingTransactionsRemoteException()
+ && !res) {
+ throw new DeadObjectException();
+ }
+ return res;
}
/** Executes all the pending transactions. */
@@ -159,12 +182,7 @@
final int size = mPendingTransactions.size();
for (int i = 0; i < size; i++) {
final ClientTransaction transaction = mPendingTransactions.valueAt(i);
- try {
- scheduleTransaction(transaction);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to deliver pending transaction", e);
- // TODO(b/323801078): apply cleanup for individual transaction item if needed.
- }
+ scheduleTransaction(transaction);
}
mPendingTransactions.clear();
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -174,12 +192,7 @@
void dispatchPendingTransaction(@NonNull IApplicationThread client) {
final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
if (pendingTransaction != null) {
- try {
- scheduleTransaction(pendingTransaction);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to deliver pending transaction", e);
- // TODO(b/323801078): apply cleanup for individual transaction item if needed.
- }
+ scheduleTransaction(pendingTransaction);
}
}
@@ -211,15 +224,22 @@
return transaction;
}
- /** Must only be called with WM lock. */
- private void onClientTransactionItemScheduled(
+ /**
+ * Must only be called with WM lock.
+ * If the transaction should not be queued, it will be dispatched immediately.
+ *
+ * @return {@code false} if the transaction was dispatched immediately, but failed because of
+ * {@link RemoteException}.
+ */
+ private boolean onClientTransactionItemScheduled(
@NonNull ClientTransaction clientTransaction,
- boolean shouldDispatchImmediately) throws RemoteException {
+ boolean shouldDispatchImmediately) {
if (shouldDispatchImmediately || shouldDispatchPendingTransactionsImmediately()) {
// Dispatch the pending transaction immediately.
mPendingTransactions.remove(clientTransaction.getClient().asBinder());
- scheduleTransaction(clientTransaction);
+ return scheduleTransaction(clientTransaction);
}
+ return true;
}
/** Must only be called with WM lock. */
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 301a754..9d4fe9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -41,8 +41,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
import static android.content.pm.ApplicationInfo.CATEGORY_GAME;
+import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
@@ -66,7 +66,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
-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.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -196,7 +195,7 @@
// Because the booted state is set, avoid starting real home if there is no task.
doReturn(false).when(mRootWindowContainer).resumeHomeActivity(any(), anyString(), any());
// Do not execute the transaction, because we can't verify the parameter after it recycles.
- doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
+ doReturn(true).when(mClientLifecycleManager).scheduleTransaction(any());
}
private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
@@ -266,7 +265,7 @@
break;
}
}
- return null;
+ return true;
}).when(mClientLifecycleManager).scheduleTransaction(any());
activity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
index 02ad9db..8d214dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java
@@ -16,11 +16,13 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -32,17 +34,19 @@
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -60,15 +64,11 @@
@Mock
private IApplicationThread mClient;
@Mock
- private IApplicationThread.Stub mNonBinderClient;
- @Mock
private ClientTransaction mTransaction;
@Mock
private ClientTransactionItem mTransactionItem;
@Mock
private ActivityLifecycleItem mLifecycleItem;
- @Captor
- private ArgumentCaptor<ClientTransaction> mTransactionCaptor;
private WindowManagerService mWms;
private ClientLifecycleManager mLifecycleManager;
@@ -83,7 +83,6 @@
doReturn(true).when(mLifecycleItem).isActivityLifecycleItem();
doReturn(mClientBinder).when(mClient).asBinder();
- doReturn(mNonBinderClient).when(mNonBinderClient).asBinder();
}
@Test
@@ -91,13 +90,11 @@
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mTransactionItem);
+ mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
// When there is traversal scheduled, add transaction items to pending.
assertEquals(1, mLifecycleManager.mPendingTransactions.size());
- ClientTransaction transaction =
- mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+ ClientTransaction transaction = mLifecycleManager.mPendingTransactions.get(mClientBinder);
assertEquals(1, transaction.getTransactionItems().size());
assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@@ -108,10 +105,10 @@
// Add new transaction item to the existing pending.
clearInvocations(mLifecycleManager);
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+ mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
assertEquals(1, mLifecycleManager.mPendingTransactions.size());
- transaction = mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+ transaction = mLifecycleManager.mPendingTransactions.get(mClientBinder);
assertEquals(2, transaction.getTransactionItems().size());
assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1));
@@ -124,8 +121,7 @@
@Test
public void testScheduleTransactionItemNow() throws RemoteException {
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItemNow(mNonBinderClient, mTransactionItem);
+ mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
// Dispatch immediately.
assertTrue(mLifecycleManager.mPendingTransactions.isEmpty());
@@ -137,13 +133,11 @@
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItems(mNonBinderClient, mTransactionItem,
- mLifecycleItem);
+ mLifecycleManager.scheduleTransactionItems(mClient, mTransactionItem, mLifecycleItem);
assertEquals(1, mLifecycleManager.mPendingTransactions.size());
final ClientTransaction transaction =
- mLifecycleManager.mPendingTransactions.get(mNonBinderClient);
+ mLifecycleManager.mPendingTransactions.get(mClientBinder);
assertEquals(2, transaction.getTransactionItems().size());
assertEquals(mTransactionItem, transaction.getTransactionItems().get(0));
assertEquals(mLifecycleItem, transaction.getTransactionItems().get(1));
@@ -160,8 +154,8 @@
spyOn(mWms.mWindowPlacerLocked);
doReturn(true).when(mWms.mWindowPlacerLocked).isTraversalScheduled();
- // Use non binder client to get non-recycled ClientTransaction.
- mLifecycleManager.scheduleTransactionItems(mNonBinderClient,
+ mLifecycleManager.scheduleTransactionItems(
+ mClient,
true /* shouldDispatchImmediately */,
mTransactionItem, mLifecycleItem);
@@ -187,7 +181,7 @@
doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
// Queue transactions during layout deferred.
- mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem);
+ mLifecycleManager.scheduleTransactionItem(mClient, mLifecycleItem);
verify(mLifecycleManager, never()).scheduleTransaction(any());
@@ -203,4 +197,42 @@
verify(mLifecycleManager).scheduleTransaction(any());
}
+
+ @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+ @Test
+ public void testOnRemoteException_returnTrueOnSuccess() throws RemoteException {
+ final boolean res = mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
+
+ assertTrue(res);
+ }
+
+ @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+ @Test
+ public void testOnRemoteException_returnFalseOnFailure() throws RemoteException {
+ final DeadObjectException e = new DeadObjectException();
+ doThrow(e).when(mClient).scheduleTransaction(any());
+
+ // No exception when flag enabled.
+ final boolean res = mLifecycleManager.scheduleTransactionItemNow(mClient, mTransactionItem);
+
+ assertFalse(res);
+ }
+
+ @EnableFlags(Flags.FLAG_CLEANUP_DISPATCH_PENDING_TRANSACTIONS_REMOTE_EXCEPTION)
+ @Test
+ public void testOnRemoteException_returnTrueForQueueing() throws RemoteException {
+ spyOn(mWms.mWindowPlacerLocked);
+ doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+ final DeadObjectException e = new DeadObjectException();
+ doThrow(e).when(mClient).scheduleTransaction(any());
+
+ final boolean res = mLifecycleManager.scheduleTransactionItem(mClient, mTransactionItem);
+
+ assertTrue(res);
+
+ doReturn(false).when(mWms.mWindowPlacerLocked).isLayoutDeferred();
+ mLifecycleManager.onLayoutContinued();
+
+ verify(mLifecycleManager).scheduleTransaction(any());
+ }
}
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 d228970..9dc7026 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -24,7 +24,6 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
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;
@@ -295,7 +294,7 @@
@Test
public void testCachedStateConfigurationChange() throws RemoteException {
- doNothing().when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
+ doReturn(true).when(mClientLifecycleManager).scheduleTransactionItemNow(any(), any());
final IApplicationThread thread = mWpc.getThread();
final Configuration newConfig = new Configuration(mWpc.getConfiguration());
newConfig.densityDpi += 100;
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
index 1bdf019..9d87fbd 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java
@@ -18,10 +18,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import android.app.jank.Flags;
import android.app.jank.JankTracker;
import android.app.jank.StateTracker;
+import android.content.Context;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -55,10 +57,9 @@
* Start an empty activity so decore view is not null when creating the JankTracker instance.
*/
private static ActivityScenario<EmptyActivity> sEmptyActivityRule;
-
private static String sActivityName;
-
private static View sActivityDecorView;
+ private static Context sContext;
@BeforeClass
public static void classSetup() {
@@ -66,6 +67,7 @@
sEmptyActivityRule.onActivity(activity -> {
sActivityDecorView = activity.getWindow().getDecorView();
sActivityName = activity.toString();
+ sContext = activity.getApplicationContext();
});
}
@@ -168,4 +170,14 @@
assertNotNull(jankTracker);
}
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API)
+ public void jankTracker_IsNull_WhenViewNotInHierarchy() {
+ TestWidget testWidget = new TestWidget(sContext);
+ JankTracker jankTracker = testWidget.getJankTracker();
+
+ assertNull(jankTracker);
+ }
+
}