Merge "Shortcut Helper cancel icon fixes." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index cbb6566..b5f398b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -58,7 +58,6 @@
":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
":com.android.net.flags-aconfig-java{.generated_srcjars}",
":device_policy_aconfig_flags_lib{.generated_srcjars}",
- ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
":surfaceflinger_flags_java_lib{.generated_srcjars}",
":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 2f67090..b497871 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -82,6 +82,7 @@
"junit",
"truth",
"ravenwood-junit",
+ "android.test.mock",
],
}
diff --git a/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
new file mode 100644
index 0000000..d8b2d75
--- /dev/null
+++ b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
Binary files differ
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index f84a0d0..e5a06c9 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -16,20 +16,29 @@
package android.graphics.perftests;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
+import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import com.android.perftests.core.R;
+
import org.junit.Rule;
import org.junit.Test;
+import java.io.IOException;
+
@LargeTest
public class CanvasPerfTest {
@Rule
@@ -93,4 +102,38 @@
node.end(canvas);
}
}
+
+ @Test
+ public void testCreateScaledBitmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ source.setGainmap(null);
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
+
+ @Test
+ public void testCreateScaledBitmapWithGainmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ assertTrue(source.hasGainmap());
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 6c83add..8b55e07 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -13,10 +13,6 @@
name: "service-jobscheduler",
installable: true,
- defaults: [
- "service-jobscheduler-aconfig-libraries",
- ],
-
srcs: [
"java/**/*.java",
":framework-jobscheduler-shared-srcs",
@@ -32,6 +28,7 @@
static_libs: [
"modules-utils-fastxmlserializer",
+ "service-jobscheduler-job.flags-aconfig-java",
],
// Rename classes shared with the framework
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 7d8a363..3ba7aa2 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -10,7 +10,6 @@
java_aconfig_library {
name: "service-jobscheduler-deviceidle.flags-aconfig-java",
aconfig_declarations: "service-deviceidle.flags-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
visibility: ["//frameworks/base:__subpackages__"],
}
@@ -26,21 +25,5 @@
java_aconfig_library {
name: "service-jobscheduler-job.flags-aconfig-java",
aconfig_declarations: "service-job.flags-aconfig",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
- visibility: ["//frameworks/base:__subpackages__"],
-}
-
-service_jobscheduler_aconfig_srcjars = [
- ":service-jobscheduler-deviceidle.flags-aconfig-java{.generated_srcjars}",
- ":service-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
-]
-
-// Aconfig declarations and libraries for the core framework
-java_defaults {
- name: "service-jobscheduler-aconfig-libraries",
- // Add java_aconfig_libraries to here to add them to the core framework
- srcs: service_jobscheduler_aconfig_srcjars,
- // Add aconfig-annotations-lib as a dependency for the optimization
- libs: ["aconfig-annotations-lib"],
visibility: ["//frameworks/base:__subpackages__"],
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 2d23533..917529e 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
+#include <getopt.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
@@ -32,6 +33,7 @@
#include <ftl/concat.h>
#include <ftl/optional.h>
+#include <gui/DisplayCaptureArgs.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
@@ -48,14 +50,17 @@
#define COLORSPACE_DISPLAY_P3 2
void usage(const char* pname, ftl::Optional<DisplayId> displayIdOpt) {
- fprintf(stderr,
- "usage: %s [-hp] [-d display-id] [FILENAME]\n"
- " -h: this message\n"
- " -p: save the file as a png.\n"
- " -d: specify the display ID to capture%s\n"
- " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
- "If FILENAME ends with .png it will be saved as a png.\n"
- "If FILENAME is not given, the results will be printed to stdout.\n",
+ fprintf(stderr, R"(
+usage: %s [-hp] [-d display-id] [FILENAME]
+ -h: this message
+ -p: save the file as a png.
+ -d: specify the display ID to capture%s
+ see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
+ --hint-for-seamless If set will use the hintForSeamless path in SF
+
+If FILENAME ends with .png it will be saved as a png.
+If FILENAME is not given, the results will be printed to stdout.
+)",
pname,
displayIdOpt
.transform([](DisplayId id) {
@@ -65,6 +70,21 @@
.c_str());
}
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ HintForSeamless,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+ {"png", no_argument, nullptr, 'p'},
+ {"help", no_argument, nullptr, 'h'},
+ {"hint-for-seamless", no_argument, nullptr, LongOpts::HintForSeamless},
+ {0, 0, 0, 0}};
+
static int32_t flinger2bitmapFormat(PixelFormat f)
{
switch (f) {
@@ -134,10 +154,11 @@
return 1;
}
std::optional<DisplayId> displayIdOpt;
+ gui::CaptureArgs captureArgs;
const char* pname = argv[0];
bool png = false;
int c;
- while ((c = getopt(argc, argv, "phd:")) != -1) {
+ while ((c = getopt_long(argc, argv, "phd:", LONG_OPTIONS, nullptr)) != -1) {
switch (c) {
case 'p':
png = true;
@@ -165,6 +186,9 @@
}
usage(pname, displayIdOpt);
return 1;
+ case LongOpts::HintForSeamless:
+ captureArgs.hintForSeamlessTransition = true;
+ break;
}
}
@@ -215,7 +239,7 @@
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- ScreenshotClient::captureDisplay(*displayIdOpt, captureListener);
+ ScreenshotClient::captureDisplay(*displayIdOpt, captureArgs, captureListener);
ScreenCaptureResults captureResults = captureListener->waitForResults();
if (!captureResults.fenceResult.ok()) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 4256d51..5f7b0c2 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10405,7 +10405,7 @@
method public abstract java.io.File getDatabasePath(String);
method public int getDeviceId();
method public abstract java.io.File getDir(String, int);
- method @Nullable public android.view.Display getDisplay();
+ method @NonNull public android.view.Display getDisplay();
method @Nullable public final android.graphics.drawable.Drawable getDrawable(@DrawableRes int);
method @Nullable public abstract java.io.File getExternalCacheDir();
method public abstract java.io.File[] getExternalCacheDirs();
@@ -12848,6 +12848,7 @@
field public static final String FEATURE_CAMERA_LEVEL_FULL = "android.hardware.camera.level.full";
field public static final String FEATURE_CANT_SAVE_STATE = "android.software.cant_save_state";
field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
+ field @FlaggedApi("android.view.inputmethod.concurrent_input_methods") public static final String FEATURE_CONCURRENT_INPUT_METHODS = "android.software.concurrent_input_methods";
field @Deprecated public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
field public static final String FEATURE_CONTROLS = "android.software.controls";
@@ -33179,6 +33180,7 @@
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+ method @FlaggedApi("android.os.allow_thermal_headroom_thresholds") @NonNull public java.util.Map<java.lang.Integer,java.lang.Float> getThermalHeadroomThresholds();
method public boolean isAllowedInLowPowerStandby(int);
method public boolean isAllowedInLowPowerStandby(@NonNull String);
method public boolean isBatteryDischargePredictionPersonalized();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index df466ab..e7803fb 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -639,3 +639,12 @@
}
+package android.view.accessibility {
+
+ public final class AccessibilityManager {
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean startFlashNotificationSequence(@NonNull android.content.Context, int);
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean stopFlashNotificationSequence(@NonNull android.content.Context);
+ }
+
+}
+
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a7ea753..5958f87 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3991,7 +3991,7 @@
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
- method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
+ method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.QUARANTINE_APPS}, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
@@ -4598,6 +4598,14 @@
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
+ public final class VirtualDisplayConfig implements android.os.Parcelable {
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
+ }
+
+ public static final class VirtualDisplayConfig.Builder {
+ method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
+ }
+
}
package android.hardware.hdmi {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 75797ed..93932e4 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3603,6 +3603,8 @@
public final class AccessibilityManager {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
method public boolean hasAnyDirectConnection();
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean startFlashNotificationSequence(@NonNull android.content.Context, int);
+ method @FlaggedApi("android.view.accessibility.flash_notification_system_api") public boolean stopFlashNotificationSequence(@NonNull android.content.Context);
}
public class AccessibilityNodeInfo implements android.os.Parcelable {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 26f1c4b..9c279c3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -2563,11 +2563,14 @@
public static final int TYPE_LOCKSCREEN = 3;
/** Launched from recents gesture handler. */
public static final int TYPE_RECENTS_ANIMATION = 4;
+ /** Launched from desktop's transition handler. */
+ public static final int TYPE_DESKTOP_ANIMATION = 5;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_LAUNCHER,
TYPE_NOTIFICATION,
TYPE_LOCKSCREEN,
+ TYPE_DESKTOP_ANIMATION
})
@Retention(RetentionPolicy.SOURCE)
public @interface SourceType {}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3ee9d692..fc3a906 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -17164,6 +17164,7 @@
*
* @hide
*/
+ @UnsupportedAppUsage
public boolean isOnboardingBugreportV2FlagEnabled() {
return onboardingBugreportV2Enabled();
}
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 0b8d1df..6b558d0 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -184,6 +184,14 @@
public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2;
/**
+ * Flag for {@link RestoreSet#backupTransportFlags} to indicate if restore should be skipped
+ * for apps that have already been launched.
+ *
+ * @hide
+ */
+ public static final int FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS = 1 << 2;
+
+ /**
* Flag for {@link BackupDataOutput#getTransportFlags()} and
* {@link FullBackupDataOutput#getTransportFlags()} only.
*
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 97a7aa4..0d73e44 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -37,6 +37,7 @@
import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
import android.content.ComponentName;
import android.content.Context;
+import android.hardware.display.VirtualDisplayConfig;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SharedMemory;
@@ -326,8 +327,8 @@
* support home activities.
*
* @see Builder#setHomeComponent
+ * @see VirtualDisplayConfig#isHomeSupported()
*/
- // TODO(b/297168328): Link to the relevant API for creating displays with home support.
@FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@Nullable
public ComponentName getHomeComponent() {
@@ -737,8 +738,9 @@
*
* @param homeComponent The component name to be used as home. If unset, then the system-
* default secondary home activity will be used.
+ *
+ * @see VirtualDisplayConfig#isHomeSupported()
*/
- // TODO(b/297168328): Link to the relevant API for creating displays with home support.
@FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME)
@NonNull
public Builder setHomeComponent(@Nullable ComponentName homeComponent) {
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 0bc459a..67759f4 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -163,6 +163,7 @@
* into an editor), then {@link Item#coerceToText(Context)} will ask the content
* provider for the clip URI as text and successfully paste the entire note.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ClipData implements Parcelable {
static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
ClipDescription.MIMETYPE_TEXT_PLAIN };
@@ -387,6 +388,7 @@
* @return Returns the item's textual representation.
*/
//BEGIN_INCLUDE(coerceToText)
+ @android.ravenwood.annotation.RavenwoodThrow
public CharSequence coerceToText(Context context) {
// If this Item has an explicit textual value, simply return that.
CharSequence text = getText();
@@ -470,6 +472,7 @@
* and other things can be retrieved.
* @return Returns the item's textual representation.
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public CharSequence coerceToStyledText(Context context) {
CharSequence text = getText();
if (text instanceof Spanned) {
@@ -520,6 +523,7 @@
* and other things can be retrieved.
* @return Returns the item's representation as HTML text.
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public String coerceToHtmlText(Context context) {
// If the item has an explicit HTML value, simply return that.
String htmlText = getHtmlText();
@@ -540,6 +544,7 @@
return text != null ? text.toString() : null;
}
+ @android.ravenwood.annotation.RavenwoodThrow
private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) {
// If this Item has a URI value, try using that.
if (mUri != null) {
@@ -1030,6 +1035,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToLeaveProcess(boolean leavingPackage) {
// Assume that callers are going to be granting permissions
prepareToLeaveProcess(leavingPackage, Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -1040,6 +1046,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToLeaveProcess(boolean leavingPackage, int intentFlags) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1060,6 +1067,7 @@
}
/** {@hide} */
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(AttributionSource source) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1073,6 +1081,7 @@
}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodThrow
public void fixUris(int contentUserHint) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
@@ -1090,6 +1099,7 @@
* Only fixing the data field of the intents
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void fixUrisLight(int contentUserHint) {
final int size = mItems.size();
for (int i = 0; i < size; i++) {
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index de2ba44..5953890 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -48,6 +48,7 @@
* developer guide.</p>
* </div>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ClipDescription implements Parcelable {
/**
* The MIME type for a clip holding plain text.
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index f12e971..a6a6bcc 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -37,6 +37,7 @@
* name inside of that package.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
private final String mPackage;
private final String mClass;
diff --git a/core/java/android/content/ContentUris.java b/core/java/android/content/ContentUris.java
index 767d3f6..093faff 100644
--- a/core/java/android/content/ContentUris.java
+++ b/core/java/android/content/ContentUris.java
@@ -70,6 +70,7 @@
*</dl>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ContentUris {
/**
diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java
index 02a5ba1..bde2f3e 100644
--- a/core/java/android/content/ContentValues.java
+++ b/core/java/android/content/ContentValues.java
@@ -35,6 +35,7 @@
* This class is used to store a set of values that the {@link ContentResolver}
* can process.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ContentValues implements Parcelable {
public static final String TAG = "ContentValues";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1c917ee..1c6c7b5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7560,7 +7560,7 @@
* @throws UnsupportedOperationException if the method is called on an instance that is not
* associated with any display.
*/
- @Nullable
+ @NonNull
public Display getDisplay() {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6b39f26..665ba11 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -660,6 +660,7 @@
* {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list
* of all possible flags.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Intent implements Parcelable, Cloneable {
private static final String TAG = "Intent";
@@ -12180,6 +12181,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void prepareToLeaveProcess(Context context) {
final boolean leavingPackage;
@@ -12201,6 +12203,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToLeaveProcess(boolean leavingPackage) {
setAllowFds(false);
@@ -12296,6 +12299,7 @@
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(boolean fromProtectedComponent, AttributionSource source) {
if (fromProtectedComponent) {
prepareToEnterProcess(LOCAL_FLAG_FROM_PROTECTED_COMPONENT, source);
@@ -12307,6 +12311,7 @@
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void prepareToEnterProcess(int localFlags, AttributionSource source) {
// We just entered destination process, so we should be able to read all
// parcelables inside.
@@ -12378,6 +12383,7 @@
/**
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public void fixUris(int contentUserHint) {
Uri data = getData();
if (data != null) {
@@ -12417,6 +12423,7 @@
* @return Whether any contents were migrated.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean migrateExtraStreamToClipData() {
return migrateExtraStreamToClipData(AppGlobals.getInitialApplication());
}
@@ -12430,6 +12437,7 @@
* @return Whether any contents were migrated.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean migrateExtraStreamToClipData(Context context) {
// Refuse to touch if extras already parcelled
if (mExtras != null && mExtras.isParcelled()) return false;
@@ -12545,6 +12553,7 @@
return false;
}
+ @android.ravenwood.annotation.RavenwoodThrow
private Uri maybeConvertFileToContentUri(Context context, Uri uri) {
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())
&& context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.R) {
@@ -12598,6 +12607,7 @@
// TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal}
/** @hide */
+ @android.ravenwood.annotation.RavenwoodThrow
public boolean isSandboxActivity(@NonNull Context context) {
if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
return true;
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index f946754..ad3acd7 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -152,6 +152,7 @@
* that unlike the action, an IntentFilter with no categories
* will only match an Intent that does not have any categories.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class IntentFilter implements Parcelable {
private static final String TAG = "IntentFilter";
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index addede4..a2cfbf5 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -57,5 +57,11 @@
],
"file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsContentTestCasesRavenwood",
+ "host": true
+ }
]
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java
index 7fa48f0..4422ade 100644
--- a/core/java/android/content/UriMatcher.java
+++ b/core/java/android/content/UriMatcher.java
@@ -119,6 +119,7 @@
}
</pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class UriMatcher
{
public static final int NO_MATCH = -1;
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
index 2096727..072ffd7 100644
--- a/core/java/android/content/pm/Checksum.java
+++ b/core/java/android/content/pm/Checksum.java
@@ -139,6 +139,13 @@
public @interface TypeMask {}
/**
+ * Max size of checksum in bytes.
+ * sizeof(SHA512) == 64 bytes
+ * @hide
+ */
+ public static final int MAX_CHECKSUM_SIZE_BYTES = 64;
+
+ /**
* Serialize checksum to the stream in binary format.
* @hide
*/
@@ -276,10 +283,10 @@
};
@DataClass.Generated(
- time = 1619810358402L,
+ time = 1700002689652L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
- inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
+ inputSignatures = "public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\npublic static final int MAX_CHECKSUM_SIZE_BYTES\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\npublic static void writeToStream(java.io.DataOutputStream,android.content.pm.Checksum)\npublic static @android.annotation.NonNull android.content.pm.Checksum readFromStream(java.io.DataInputStream)\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index a4d5327..cb3455b 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -22,11 +22,18 @@
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.graphics.Paint;
import android.graphics.drawable.Drawable;
+import android.icu.text.UnicodeSet;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
/**
* A representation of an activity that can belong to this user or a managed
* profile associated with this user. It can be used to query the label, icon
@@ -36,6 +43,10 @@
private final PackageManager mPm;
private final LauncherActivityInfoInternal mInternal;
+ private static final UnicodeSet TRIMMABLE_CHARACTERS =
+ new UnicodeSet("[[:White_Space:][:Default_Ignorable_Code_Point:][:gc=Cc:]]",
+ /* ignoreWhitespace= */ false).freeze();
+
/**
* Create a launchable activity object for a given ResolveInfo and user.
*
@@ -77,8 +88,22 @@
* @return The label for the activity.
*/
public CharSequence getLabel() {
+ if (!Flags.lightweightInvisibleLabelDetection()) {
+ // TODO: Go through LauncherAppsService
+ return getActivityInfo().loadLabel(mPm);
+ }
+
+ CharSequence label = trim(getActivityInfo().loadLabel(mPm));
+ // If the trimmed label is empty, use application's label instead
+ if (TextUtils.isEmpty(label)) {
+ label = trim(getApplicationInfo().loadLabel(mPm));
+ // If the trimmed label is still empty, use package name instead
+ if (TextUtils.isEmpty(label)) {
+ label = getComponentName().getPackageName();
+ }
+ }
// TODO: Go through LauncherAppsService
- return getActivityInfo().loadLabel(mPm);
+ return label;
}
/**
@@ -180,4 +205,149 @@
return mPm.getUserBadgedIcon(originalIcon, mInternal.getUser());
}
+
+ /**
+ * If the {@code ch} is trimmable, return {@code true}. Otherwise, return
+ * {@code false}. If the count of the code points of {@code ch} doesn't
+ * equal 1, return {@code false}.
+ * <p>
+ * There are two types of the trimmable characters.
+ * 1. The character is one of the Default_Ignorable_Code_Point in
+ * <a href="
+ * https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt">
+ * DerivedCoreProperties.txt</a>, the White_Space in <a href=
+ * "https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt">PropList.txt
+ * </a> or category Cc.
+ * <p>
+ * 2. The character is not supported in the current system font.
+ * {@link android.graphics.Paint#hasGlyph(String)}
+ * <p>
+ *
+ */
+ private static boolean isTrimmable(@NonNull Paint paint, @NonNull CharSequence ch) {
+ Objects.requireNonNull(paint);
+ Objects.requireNonNull(ch);
+
+ // if ch is empty or it is not a character (i,e, the count of code
+ // point doesn't equal one), return false
+ if (TextUtils.isEmpty(ch)
+ || Character.codePointCount(ch, /* beginIndex= */ 0, ch.length()) != 1) {
+ return false;
+ }
+
+ // Return true for the cases as below:
+ // 1. The character is in the TRIMMABLE_CHARACTERS set
+ // 2. The character is not supported in the system font
+ return TRIMMABLE_CHARACTERS.contains(ch) || !paint.hasGlyph(ch.toString());
+ }
+
+ /**
+ * If the {@code sequence} has some leading trimmable characters, creates a new copy
+ * and removes the trimmable characters from the copy. Otherwise the given
+ * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
+ * to determine whether the character is trimmable or not.
+ *
+ * @return the trimmed string or the original string that has no
+ * leading trimmable characters.
+ * @see #isTrimmable(Paint, CharSequence)
+ * @see #trim(CharSequence)
+ * @see #trimEnd(CharSequence)
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CharSequence trimStart(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+
+ if (TextUtils.isEmpty(sequence)) {
+ return sequence;
+ }
+
+ final Paint paint = new Paint();
+ int trimCount = 0;
+ final int[] codePoints = sequence.codePoints().toArray();
+ for (int i = 0, length = codePoints.length; i < length; i++) {
+ String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
+ if (!isTrimmable(paint, ch)) {
+ break;
+ }
+ trimCount += ch.length();
+ }
+ if (trimCount == 0) {
+ return sequence;
+ }
+ return sequence.subSequence(trimCount, sequence.length());
+ }
+
+ /**
+ * If the {@code sequence} has some trailing trimmable characters, creates a new copy
+ * and removes the trimmable characters from the copy. Otherwise the given
+ * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
+ * to determine whether the character is trimmable or not.
+ *
+ * @return the trimmed sequence or the original sequence that has no
+ * trailing trimmable characters.
+ * @see #isTrimmable(Paint, CharSequence)
+ * @see #trimStart(CharSequence)
+ * @see #trim(CharSequence)
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CharSequence trimEnd(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+
+ if (TextUtils.isEmpty(sequence)) {
+ return sequence;
+ }
+
+ final Paint paint = new Paint();
+ int trimCount = 0;
+ final int[] codePoints = sequence.codePoints().toArray();
+ for (int i = codePoints.length - 1; i >= 0; i--) {
+ String ch = new String(new int[]{codePoints[i]}, /* offset= */ 0, /* count= */ 1);
+ if (!isTrimmable(paint, ch)) {
+ break;
+ }
+ trimCount += ch.length();
+ }
+
+ if (trimCount == 0) {
+ return sequence;
+ }
+ return sequence.subSequence(0, sequence.length() - trimCount);
+ }
+
+ /**
+ * If the {@code sequence} has some leading or trailing trimmable characters, creates
+ * a new copy and removes the trimmable characters from the copy. Otherwise the given
+ * {@code sequence} is returned as it is. Use {@link #isTrimmable(Paint, CharSequence)}
+ * to determine whether the character is trimmable or not.
+ *
+ * @return the trimmed sequence or the original sequence that has no leading or
+ * trailing trimmable characters.
+ * @see #isTrimmable(Paint, CharSequence)
+ * @see #trimStart(CharSequence)
+ * @see #trimEnd(CharSequence)
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ @NonNull
+ public static CharSequence trim(@NonNull CharSequence sequence) {
+ Objects.requireNonNull(sequence);
+
+ if (TextUtils.isEmpty(sequence)) {
+ return sequence;
+ }
+
+ CharSequence result = trimStart(sequence);
+ if (TextUtils.isEmpty(result)) {
+ return result;
+ }
+
+ return trimEnd(result);
+ }
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 72e1066..fe66759 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4019,6 +4019,15 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports multiple concurrent IME sessions.
+ */
+ @FlaggedApi("android.view.inputmethod.concurrent_input_methods")
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CONCURRENT_INPUT_METHODS =
+ "android.software.concurrent_input_methods";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports device policy enforcement via device admins.
*/
@SdkConstant(SdkConstantType.FEATURE)
@@ -9781,7 +9790,8 @@
* launcher to support customization that they might need to handle the suspended state.
*
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API except for
- * device owner and profile owner.
+ * device owner and profile owner or the {@link Manifest.permission#QUARANTINE_APPS} if the
+ * caller is using {@link #FLAG_SUSPEND_QUARANTINED}.
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -9809,7 +9819,10 @@
*/
@SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED)
- @RequiresPermission(value=Manifest.permission.SUSPEND_APPS, conditional=true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.SUSPEND_APPS,
+ Manifest.permission.QUARANTINE_APPS
+ }, conditional = true)
@SuppressLint("NullableCollection")
@Nullable
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 814eae6..bb5fdb7 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -80,3 +80,10 @@
description: "Feature flag to retrieve resolved path of the base APK during an app install."
bug: "269728874"
}
+
+flag {
+ name: "lightweight_invisible_label_detection"
+ namespace: "package_manager_service"
+ description: "Feature flag to detect the invisible labels in Launcher Apps"
+ bug: "299586370"
+}
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index 1667896..38dbec5 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -38,9 +38,11 @@
private static final int MAX_ATTR_LEN_PERMISSION_GROUP = 256;
private static final int MAX_ATTR_LEN_PACKAGE = 256;
private static final int MAX_ATTR_LEN_MIMETYPE = 512;
- public static final int MAX_ATTR_LEN_NAME = 1024;
- public static final int MAX_ATTR_LEN_PATH = 4000;
- public static final int MAX_ATTR_LEN_VALUE = 32_768;
+ private static final int MAX_ATTR_LEN_NAME = 1024;
+ private static final int MAX_ATTR_LEN_PATH = 4000;
+ private static final int MAX_ATTR_LEN_VALUE = 32_768;
+
+ private static final int MAX_TOTAL_META_DATA_SIZE = 262_144;
private static final String BAD_COMPONENT_NAME_CHARS = ";,[](){}:?%^*|/\\";
@@ -157,6 +159,7 @@
}
private long mChildTagMask = 0;
+ private int mTotalComponentMetadataSize = 0;
private static int getCounterIdx(String tag) {
switch(tag) {
@@ -283,6 +286,7 @@
private void init(String tag) {
this.mTag = tag;
mChildTagMask = 0;
+ mTotalComponentMetadataSize = 0;
switch (tag) {
case TAG_ACTIVITY:
initializeCounter(TAG_LAYOUT, 1000);
@@ -820,6 +824,12 @@
}
}
+ void validateComponentMetadata(String value) {
+ mTotalComponentMetadataSize += value.length();
+ if (mTotalComponentMetadataSize > MAX_TOTAL_META_DATA_SIZE) {
+ throw new SecurityException("Max total meta data size limit exceeded for " + mTag);
+ }
+ }
void seen(@NonNull Element element) {
TagCounter counter = mTagCounters[getCounterIdx(element.mTag)];
diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java
index cae353b..3b68452 100644
--- a/core/java/android/content/res/Validator.java
+++ b/core/java/android/content/res/Validator.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.StyleableRes;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -84,6 +86,9 @@
return;
}
mElements.peek().validateResStrAttr(index, stringValue);
+ if (index == R.styleable.AndroidManifestMetaData_value) {
+ validateComponentMetadata(stringValue.toString());
+ }
}
/**
@@ -94,5 +99,20 @@
return;
}
mElements.peek().validateStrAttr(attrName, attrValue);
+ if (attrName.equals(Element.TAG_ATTR_VALUE)) {
+ validateComponentMetadata(attrValue);
+ }
+ }
+
+ private void validateComponentMetadata(String attrValue) {
+ Element element = mElements.peek();
+ // Meta-data values are evaluated on the parent element which is the next element in the
+ // mElements stack after the meta-data element. The top of the stack is always the current
+ // element being validated so check that the top element is meta-data.
+ if (element.mTag.equals(Element.TAG_META_DATA) && mElements.size() > 1) {
+ element = mElements.pop();
+ mElements.peek().validateComponentMetadata(attrValue);
+ mElements.push(element);
+ }
}
}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index ec96215..bab84aa 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -20,3 +20,10 @@
description: "Enables clearing of Credential Manager sessions when client process dies"
bug: "308470501"
}
+
+flag {
+ namespace: "credential_manager"
+ name: "new_settings_intents"
+ description: "Enables settings intents to redirect to new settings page"
+ bug: "307587989"
+}
\ No newline at end of file
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 69d573f..80f085f 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -33,11 +33,11 @@
import java.util.Map;
import java.util.Objects;
-
/**
* This is an abstract cursor class that handles a lot of the common code
* that all cursors need to deal with and is provided for convenience reasons.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class AbstractCursor implements CrossProcessCursor {
private static final String TAG = "Cursor";
@@ -89,7 +89,7 @@
private Bundle mExtras = Bundle.EMPTY;
/** CloseGuard to detect leaked cursor **/
- private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final CloseGuard mCloseGuard;
/* -------------------------------------------------------- */
/* These need to be implemented by subclasses */
@@ -184,7 +184,9 @@
mClosed = true;
mContentObservable.unregisterAll();
onDeactivateOrClose();
- mCloseGuard.close();
+ if (mCloseGuard != null) {
+ mCloseGuard.close();
+ }
}
/**
@@ -224,7 +226,19 @@
/* Implementation */
public AbstractCursor() {
mPos = -1;
- mCloseGuard.open("AbstractCursor.close");
+ mCloseGuard = initCloseGuard();
+ if (mCloseGuard != null) {
+ mCloseGuard.open("AbstractCursor.close");
+ }
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private CloseGuard initCloseGuard() {
+ return CloseGuard.get();
+ }
+
+ private CloseGuard initCloseGuard$ravenwood() {
+ return null;
}
@Override
diff --git a/core/java/android/database/CharArrayBuffer.java b/core/java/android/database/CharArrayBuffer.java
index 73781b7..4927654 100644
--- a/core/java/android/database/CharArrayBuffer.java
+++ b/core/java/android/database/CharArrayBuffer.java
@@ -19,6 +19,7 @@
/**
* This is used for {@link Cursor#copyStringToBuffer}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CharArrayBuffer {
public CharArrayBuffer(int size) {
data = new char[size];
diff --git a/core/java/android/database/ContentObservable.java b/core/java/android/database/ContentObservable.java
index 7692bb3..dc35b5a 100644
--- a/core/java/android/database/ContentObservable.java
+++ b/core/java/android/database/ContentObservable.java
@@ -23,6 +23,7 @@
* that provides methods for sending notifications to a list of
* {@link ContentObserver} objects.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ContentObservable extends Observable<ContentObserver> {
// Even though the generic method defined in Observable would be perfectly
// fine on its own, we can't delete this overridden method because it would
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index c27ee51..39c9400 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -36,6 +36,7 @@
* Receives call backs for changes to content.
* Must be implemented by objects which are added to a {@link ContentObservable}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ContentObserver {
/**
* Starting in {@link android.os.Build.VERSION_CODES#R}, there is a new
@@ -49,7 +50,6 @@
@ChangeId
@EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
private static final long ADD_CONTENT_OBSERVER_FLAGS = 150939131L;
-
private final Object mLock = new Object();
private Transport mTransport; // guarded by mLock
@@ -216,7 +216,7 @@
// There are dozens of people relying on the hidden API inside the
// system UID, so hard-code the old behavior for all of them; for
// everyone else we gate based on a specific change
- if (!CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS)
+ if (!isChangeEnabledAddContentObserverFlags()
|| android.os.Process.myUid() == android.os.Process.SYSTEM_UID) {
// Deliver userId through argument to preserve hidden API behavior
onChange(selfChange, uris, flags, UserHandle.of(userId));
@@ -225,6 +225,15 @@
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static boolean isChangeEnabledAddContentObserverFlags() {
+ return CompatChanges.isChangeEnabled(ADD_CONTENT_OBSERVER_FLAGS);
+ }
+
+ private static boolean isChangeEnabledAddContentObserverFlags$ravenwood() {
+ return true;
+ }
+
/**
* Dispatches a change notification to the observer.
* <p>
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index afa1c20..cb1d3f5 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -40,6 +40,7 @@
* Implementations should subclass {@link AbstractCursor}.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Cursor extends Closeable {
/*
* Values returned by {@link #getType(int)}.
diff --git a/core/java/android/database/CursorIndexOutOfBoundsException.java b/core/java/android/database/CursorIndexOutOfBoundsException.java
index 1f77d00..89d4418 100644
--- a/core/java/android/database/CursorIndexOutOfBoundsException.java
+++ b/core/java/android/database/CursorIndexOutOfBoundsException.java
@@ -19,6 +19,7 @@
/**
* An exception indicating that a cursor is out of bounds.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CursorIndexOutOfBoundsException extends IndexOutOfBoundsException {
public CursorIndexOutOfBoundsException(int index, int size) {
diff --git a/core/java/android/database/CursorJoiner.java b/core/java/android/database/CursorJoiner.java
index a95263b..2eb81a1 100644
--- a/core/java/android/database/CursorJoiner.java
+++ b/core/java/android/database/CursorJoiner.java
@@ -42,6 +42,7 @@
* }
* </pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CursorJoiner
implements Iterator<CursorJoiner.Result>, Iterable<CursorJoiner.Result> {
private Cursor mCursorLeft;
diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java
index 4496f80..6572f99 100644
--- a/core/java/android/database/CursorWrapper.java
+++ b/core/java/android/database/CursorWrapper.java
@@ -27,6 +27,7 @@
* Wrapper class for Cursor that delegates all calls to the actual cursor object. The primary
* use for this class is to extend a cursor while overriding only a subset of its methods.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CursorWrapper implements Cursor {
/** @hide */
@UnsupportedAppUsage
diff --git a/core/java/android/database/DataSetObservable.java b/core/java/android/database/DataSetObservable.java
index ca77a13..ff47f9a 100644
--- a/core/java/android/database/DataSetObservable.java
+++ b/core/java/android/database/DataSetObservable.java
@@ -21,6 +21,7 @@
* that provides methods for sending notifications to a list of
* {@link DataSetObserver} objects.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* Invokes {@link DataSetObserver#onChanged} on each observer.
diff --git a/core/java/android/database/DataSetObserver.java b/core/java/android/database/DataSetObserver.java
index 28616c8..13469cb 100644
--- a/core/java/android/database/DataSetObserver.java
+++ b/core/java/android/database/DataSetObserver.java
@@ -21,6 +21,7 @@
* that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
* DataSetObserver must be implemented by objects which are added to a DataSetObservable.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index 050a49a..ad35b2f 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -26,6 +26,7 @@
* {@link #newRow()} to add rows. Automatically expands internal capacity
* as needed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MatrixCursor extends AbstractCursor {
private final String[] columnNames;
diff --git a/core/java/android/database/MergeCursor.java b/core/java/android/database/MergeCursor.java
index 272cfa2..5a56756 100644
--- a/core/java/android/database/MergeCursor.java
+++ b/core/java/android/database/MergeCursor.java
@@ -22,6 +22,7 @@
* may be different if that is desired. Calls to getColumns, getColumnIndex, etc will return the
* value for the row that the MergeCursor is currently pointing at.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MergeCursor extends AbstractCursor
{
private DataSetObserver mObserver = new DataSetObserver() {
diff --git a/core/java/android/database/Observable.java b/core/java/android/database/Observable.java
index aff32db..a3057ac 100644
--- a/core/java/android/database/Observable.java
+++ b/core/java/android/database/Observable.java
@@ -26,6 +26,7 @@
*
* @param T The observer type.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class Observable<T> {
/**
* The list of observers. An observer can be in the list at most
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index acdc0fa..d33eadc 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -27,7 +27,6 @@
* <p>
* This class is not thread-safe.
* </p>
- * Note that this class is unrelated to {@link SQLiteRawStatement}.
*/
public final class SQLiteStatement extends SQLiteProgram {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index a978bd8..0a61c32 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -736,6 +736,9 @@
return generateJpegSupportedSizes(
extenders.second.getSupportedPostviewResolutions(sz),
streamMap);
+ } else if (format == ImageFormat.JPEG_R) {
+ // Jpeg_R/UltraHDR is currently not supported in the basic extension case
+ return new ArrayList<>();
} else {
throw new IllegalArgumentException("Unsupported format: " + format);
}
@@ -891,6 +894,9 @@
} else {
return generateSupportedSizes(null, format, streamMap);
}
+ } else if (format == ImageFormat.JPEG_R) {
+ // Jpeg_R/UltraHDR is currently not supported in the basic extension case
+ return new ArrayList<>();
} else {
throw new IllegalArgumentException("Unsupported format: " + format);
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 22e3938..7388b5b 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -18,10 +18,12 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.hardware.display.DisplayManager.VirtualDisplayFlag;
import android.media.projection.MediaProjection;
import android.os.Handler;
@@ -55,6 +57,7 @@
private final boolean mWindowManagerMirroringEnabled;
private ArraySet<String> mDisplayCategories = null;
private final float mRequestedRefreshRate;
+ private final boolean mIsHomeSupported;
private VirtualDisplayConfig(
@NonNull String name,
@@ -67,7 +70,8 @@
int displayIdToMirror,
boolean windowManagerMirroringEnabled,
@NonNull ArraySet<String> displayCategories,
- float requestedRefreshRate) {
+ float requestedRefreshRate,
+ boolean isHomeSupported) {
mName = name;
mWidth = width;
mHeight = height;
@@ -79,6 +83,7 @@
mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
mDisplayCategories = displayCategories;
mRequestedRefreshRate = requestedRefreshRate;
+ mIsHomeSupported = isHomeSupported;
}
/**
@@ -157,6 +162,18 @@
}
/**
+ * Whether this virtual display supports showing home activity and wallpaper.
+ *
+ * @see Builder#setHomeSupported
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
+ @SystemApi
+ public boolean isHomeSupported() {
+ return android.companion.virtual.flags.Flags.vdmCustomHome() && mIsHomeSupported;
+ }
+
+ /**
* Returns the display categories.
*
* @see Builder#setDisplayCategories
@@ -189,6 +206,7 @@
dest.writeBoolean(mWindowManagerMirroringEnabled);
dest.writeArraySet(mDisplayCategories);
dest.writeFloat(mRequestedRefreshRate);
+ dest.writeBoolean(mIsHomeSupported);
}
@Override
@@ -213,7 +231,8 @@
&& mDisplayIdToMirror == that.mDisplayIdToMirror
&& mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
&& Objects.equals(mDisplayCategories, that.mDisplayCategories)
- && mRequestedRefreshRate == that.mRequestedRefreshRate;
+ && mRequestedRefreshRate == that.mRequestedRefreshRate
+ && mIsHomeSupported == that.mIsHomeSupported;
}
@Override
@@ -221,7 +240,7 @@
int hashCode = Objects.hash(
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
- mRequestedRefreshRate);
+ mRequestedRefreshRate, mIsHomeSupported);
return hashCode;
}
@@ -240,6 +259,7 @@
+ " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
+ " mDisplayCategories=" + mDisplayCategories
+ " mRequestedRefreshRate=" + mRequestedRefreshRate
+ + " mIsHomeSupported=" + mIsHomeSupported
+ ")";
}
@@ -255,6 +275,7 @@
mWindowManagerMirroringEnabled = in.readBoolean();
mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
mRequestedRefreshRate = in.readFloat();
+ mIsHomeSupported = in.readBoolean();
}
@NonNull
@@ -286,6 +307,7 @@
private boolean mWindowManagerMirroringEnabled = false;
private ArraySet<String> mDisplayCategories = new ArraySet<>();
private float mRequestedRefreshRate = 0.0f;
+ private boolean mIsHomeSupported = false;
/**
* Creates a new Builder.
@@ -422,6 +444,27 @@
}
/**
+ * Sets whether this display supports showing home activities and wallpaper.
+ *
+ * <p>If set to {@code true}, then the home activity relevant to this display will be
+ * automatically launched upon the display creation.</p>
+ *
+ * <p>Note: setting to {@code true} requires the display to be trusted. If the display is
+ * not trusted, this property is ignored.</p>
+ *
+ * @param isHomeSupported whether home activities are supported on the display
+ * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
+ * @hide
+ */
+ @FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_HOME)
+ @SystemApi
+ @NonNull
+ public Builder setHomeSupported(boolean isHomeSupported) {
+ mIsHomeSupported = isHomeSupported;
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -437,7 +480,8 @@
mDisplayIdToMirror,
mWindowManagerMirroringEnabled,
mDisplayCategories,
- mRequestedRefreshRate);
+ mRequestedRefreshRate,
+ mIsHomeSupported);
}
}
}
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java
index 4d89508..c0102bf 100644
--- a/core/java/android/hardware/input/VirtualKeyEvent.java
+++ b/core/java/android/hardware/input/VirtualKeyEvent.java
@@ -172,6 +172,7 @@
KeyEvent.KEYCODE_BREAK,
KeyEvent.KEYCODE_BACK,
KeyEvent.KEYCODE_FORWARD,
+ KeyEvent.KEYCODE_LANGUAGE_SWITCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SupportedKeycode {
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index d959240..4a5c4c8 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -646,12 +646,7 @@
* @return array including {@link #COMPLIANCE_WARNING_OTHER},
* {@link #COMPLIANCE_WARNING_DEBUG_ACCESSORY},
* {@link #COMPLIANCE_WARNING_BC_1_2},
- * {@link #COMPLIANCE_WARNING_MISSING_RP},
- * {@link #COMPLIANCE_WARNING_INPUT_POWER_LIMITED},
- * {@link #COMPLIANCE_WARNING_MISSING_DATA_LINES},
- * {@link #COMPLIANCE_WARNING_ENUMERATION_FAIL},
- * {@link #COMPLIANCE_WARNING_FLAKY_CONNECTION},
- * {@link #COMPLIANCE_WARNING_UNRELIABLE_IO}.
+ * {@link #COMPLIANCE_WARNING_MISSING_RP}.
*/
@CheckResult
@NonNull
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 0a6be20..eda80c8 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,14 +85,14 @@
/**
* Interface data activity status is changed.
*
- * @param transportType The transport type of the data activity change.
+ * @param label label of the data activity change.
* @param active True if the interface is actively transmitting data, false if it is idle.
* @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
* @param uid Uid of this event. It represents the uid that was responsible for waking the
* radio. For those events that are reported by system itself, not from specific uid,
* use -1 for the events which means no uid.
*/
- void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid);
+ void interfaceClassDataActivityChanged(int label, boolean active, long tsNanos, int uid);
/**
* Information about available DNS servers has been received.
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
index 65ed618..61aa222 100644
--- a/core/java/android/os/DeadObjectException.java
+++ b/core/java/android/os/DeadObjectException.java
@@ -19,8 +19,29 @@
/**
* The object you are calling has died, because its hosting process
- * no longer exists. This is also thrown for low-level binder
- * errors.
+ * no longer exists, or there has been a low-level binder error.
+ *
+ * If you get this exception from a system service, the error is
+ * usually nonrecoverable as the framework will restart. If you
+ * receive this error from an app, at a minimum, you should
+ * recover by resetting the connection. For instance, you should
+ * drop the binder, clean up associated state, and reset your
+ * connection to the service which through this error. In order
+ * to simplify your error recovery paths, you may also want to
+ * "simply" restart your process. However, this may not be an
+ * option if the service you are talking to is unreliable or
+ * crashes frequently.
+ *
+ * If this isn't from a service death and is instead from a
+ * low-level binder error, it will be from:
+ * - a oneway call queue filling up (too many oneway calls)
+ * - from the binder buffer being filled up, so that the transaction
+ * is rejected.
+ *
+ * In these cases, more information about the error will be
+ * logged. However, there isn't a good way to differentiate
+ * this information at runtime. So, you should handle the
+ * error, as if the service died.
*/
public class DeadObjectException extends RemoteException {
public DeadObjectException() {
diff --git a/core/java/android/os/DeadSystemRuntimeException.java b/core/java/android/os/DeadSystemRuntimeException.java
index 82b1ad8..3b10798 100644
--- a/core/java/android/os/DeadSystemRuntimeException.java
+++ b/core/java/android/os/DeadSystemRuntimeException.java
@@ -19,10 +19,12 @@
/**
* Exception thrown when a call into system_server resulted in a
* DeadObjectException, meaning that the system_server has died or
- * experienced a low-level binder error. There's * nothing apps can
+ * experienced a low-level binder error. There's nothing apps can
* do at this point - the system will automatically restart - so
* there's no point in catching this.
*
+ * See {@link android.os.DeadObjectException}.
+ *
* @hide
*/
public class DeadSystemRuntimeException extends RuntimeException {
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index c6c8adc..bcffa45 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -111,4 +111,9 @@
* occur; returns NaN if the headroom or forecast is unavailable
*/
float getThermalHeadroom(int forecastSeconds);
+
+ /**
+ * @return thermal headroom for each thermal status
+ */
+ float[] getThermalHeadroomThresholds();
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d2c1755..11bddfb 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -20,6 +20,7 @@
import android.Manifest.permission;
import android.annotation.CallbackExecutor;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -42,14 +43,17 @@
import com.android.internal.util.Preconditions;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -1180,6 +1184,8 @@
private final ArrayMap<OnThermalStatusChangedListener, IThermalStatusListener>
mListenerMap = new ArrayMap<>();
+ private final Object mThermalHeadroomThresholdsLock = new Object();
+ private float[] mThermalHeadroomThresholds = null;
/**
* {@hide}
@@ -2636,6 +2642,7 @@
public static final int THERMAL_STATUS_SHUTDOWN = Temperature.THROTTLING_SHUTDOWN;
/** @hide */
+ @Target(ElementType.TYPE_USE)
@IntDef(prefix = { "THERMAL_STATUS_" }, value = {
THERMAL_STATUS_NONE,
THERMAL_STATUS_LIGHT,
@@ -2800,6 +2807,63 @@
}
/**
+ * Gets the thermal headroom thresholds for all available thermal throttling status above
+ * {@link #THERMAL_STATUS_NONE}.
+ * <p>
+ * A thermal status key in the returned map is only set if the device manufacturer has the
+ * corresponding threshold defined for at least one of its sensors. If it's set, one should
+ * expect to see that from {@link #getCurrentThermalStatus()} or
+ * {@link OnThermalStatusChangedListener#onThermalStatusChanged(int)}.
+ * <p>
+ * The headroom threshold is used to interpret the possible thermal throttling status based on
+ * the headroom prediction. For example, if the headroom threshold for
+ * {@link #THERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
+ * (or {@code getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system could
+ * be in lightly throttled state if the workload remains the same. The app can consider
+ * taking actions according to the nearest throttling status the difference between the headroom
+ * and the threshold.
+ * <p>
+ * For new devices it's guaranteed to have a single sensor, but for older devices with multiple
+ * sensors reporting different threshold values, the minimum threshold is taken to be
+ * conservative on predictions. Thus, when reading real-time headroom, it's not guaranteed that
+ * a real-time value of 0.75 (or {@code getThermalHeadroom(0)}=0.75) exceeding the threshold of
+ * 0.7 above will always come with lightly throttled state
+ * (or {@code getCurrentThermalStatus()=THERMAL_STATUS_LIGHT}) but it can be lower
+ * (or {@code getCurrentThermalStatus()=THERMAL_STATUS_NONE}). While it's always guaranteed that
+ * the device won't be throttled heavier than the unmet threshold's state, so a real-time
+ * headroom of 0.75 will never come with {@link #THERMAL_STATUS_MODERATE} but lower, and 0.65
+ * will never come with {@link #THERMAL_STATUS_LIGHT} but {@link #THERMAL_STATUS_NONE}.
+ * <p>
+ * The returned map of thresholds will not change between calls to this function, so it's
+ * best to call this once on initialization. Modifying the result will not change the thresholds
+ * cached by the system, and a new call to the API will get a new copy.
+ *
+ * @return map from each thermal status to its thermal headroom
+ * @throws IllegalStateException if the thermal service is not ready
+ * @throws UnsupportedOperationException if the feature is not enabled
+ */
+ @FlaggedApi(Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS)
+ public @NonNull Map<@ThermalStatus Integer, Float> getThermalHeadroomThresholds() {
+ try {
+ synchronized (mThermalHeadroomThresholdsLock) {
+ if (mThermalHeadroomThresholds == null) {
+ mThermalHeadroomThresholds = mThermalService.getThermalHeadroomThresholds();
+ }
+ final ArrayMap<Integer, Float> ret = new ArrayMap<>(THERMAL_STATUS_SHUTDOWN);
+ for (int status = THERMAL_STATUS_LIGHT; status <= THERMAL_STATUS_SHUTDOWN;
+ status++) {
+ if (!Float.isNaN(mThermalHeadroomThresholds[status])) {
+ ret.put(status, mThermalHeadroomThresholds[status]);
+ }
+ }
+ return ret;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* If true, the doze component is not started until after the screen has been
* turned off and the screen off animation has been performed.
* @hide
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index daec1721..13572fb 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -832,10 +832,16 @@
/**
* Returns true if the current process is a 64-bit runtime.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final boolean is64Bit() {
return VMRuntime.getRuntime().is64Bit();
}
+ /** @hide */
+ public static final boolean is64Bit$ravenwood() {
+ return "amd64".equals(System.getProperty("os.arch"));
+ }
+
private static SomeArgs sIdentity$ravenwood;
/** @hide */
@@ -906,6 +912,7 @@
* {@link #myUid()} in that a particular user will have multiple
* distinct apps running under it each with their own uid.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static UserHandle myUserHandle() {
return UserHandle.of(UserHandle.getUserId(myUid()));
}
@@ -914,6 +921,7 @@
* Returns whether the given uid belongs to a system core component or not.
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isCoreUid(int uid) {
return UserHandle.isCore(uid);
}
@@ -924,6 +932,7 @@
* @return Whether the uid corresponds to an application sandbox running in
* a specific user.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static boolean isApplicationUid(int uid) {
return UserHandle.isApp(uid);
}
@@ -931,6 +940,7 @@
/**
* Returns whether the current process is in an isolated sandbox.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isIsolated() {
return isIsolated(myUid());
}
@@ -942,6 +952,7 @@
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
publicAlternatives = "Use {@link #isIsolatedUid(int)} instead.")
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isIsolated(int uid) {
return isIsolatedUid(uid);
}
@@ -949,6 +960,7 @@
/**
* Returns whether the process with the given {@code uid} is an isolated sandbox.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isIsolatedUid(int uid) {
uid = UserHandle.getAppId(uid);
return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID)
@@ -962,6 +974,7 @@
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isSdkSandboxUid(int uid) {
uid = UserHandle.getAppId(uid);
return (uid >= FIRST_SDK_SANDBOX_UID && uid <= LAST_SDK_SANDBOX_UID);
@@ -975,6 +988,7 @@
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int getAppUidForSdkSandboxUid(int uid) {
return uid - (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
@@ -987,6 +1001,7 @@
*/
@SystemApi(client = MODULE_LIBRARIES)
@TestApi
+ @android.ravenwood.annotation.RavenwoodKeep
public static final int toSdkSandboxUid(int uid) {
return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID);
}
@@ -994,6 +1009,7 @@
/**
* Returns whether the current process is a sdk sandbox process.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static final boolean isSdkSandbox() {
return isSdkSandboxUid(myUid());
}
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 2d6e09a..b5029a6 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -153,5 +153,11 @@
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests"
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsOsTestCasesRavenwood",
+ "host": true
+ }
]
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index cac7f3b..0644ef1 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -36,6 +36,7 @@
/**
* Representation of a user on the device.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class UserHandle implements Parcelable {
// NOTE: keep logic in sync with system/core/libcutils/multiuser.c
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 0809b3b..d405d1d 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -29,6 +29,13 @@
}
flag {
+ name: "allow_thermal_headroom_thresholds"
+ namespace: "game"
+ description: "Enable thermal headroom thresholds API"
+ bug: "288119641"
+}
+
+flag {
name: "allow_private_profile"
namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index fb2ac711..dc86e3f5 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -17,6 +17,7 @@
flag {
name: "role_controller_in_system_server"
+ is_fixed_read_only: true
namespace: "permissions"
description: "enable role controller in system server"
bug: "302562590"
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 43c38f3..a1885ae 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -75,3 +75,10 @@
description: "A feature flag that implements line break word style auto."
bug: "280005585"
}
+
+flag {
+ name: "inter_character_justification"
+ namespace: "text"
+ description: "A feature flag that implement inter character justification."
+ bug: "283193133"
+}
diff --git a/core/java/android/util/TEST_MAPPING b/core/java/android/util/TEST_MAPPING
index 0ae1c15..c681f86 100644
--- a/core/java/android/util/TEST_MAPPING
+++ b/core/java/android/util/TEST_MAPPING
@@ -24,5 +24,11 @@
],
"file_patterns": ["Xml"]
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsUtilTestCasesRavenwood",
+ "host": true
+ }
]
}
diff --git a/core/java/android/util/Xml.java b/core/java/android/util/Xml.java
index 33058d8..2a33caa 100644
--- a/core/java/android/util/Xml.java
+++ b/core/java/android/util/Xml.java
@@ -26,6 +26,7 @@
import com.android.internal.util.ArtBinaryXmlSerializer;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.BinaryXmlPullParser;
import com.android.modules.utils.BinaryXmlSerializer;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -38,6 +39,7 @@
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
@@ -115,6 +117,7 @@
/**
* Returns a new pull parser with namespace support.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static XmlPullParser newPullParser() {
try {
XmlPullParser parser = XmlObjectFactory.newXmlPullParser();
@@ -126,6 +129,12 @@
}
}
+ /** @hide */
+ public static XmlPullParser newPullParser$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlPullParser();
+ }
+
/**
* Creates a new {@link TypedXmlPullParser} which is optimized for use
* inside the system, typically by supporting only a basic set of features.
@@ -136,10 +145,17 @@
* @hide
*/
@SuppressWarnings("AndroidFrameworkEfficientXml")
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlPullParser newFastPullParser() {
return XmlUtils.makeTyped(newPullParser());
}
+ /** @hide */
+ public static TypedXmlPullParser newFastPullParser$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlPullParser();
+ }
+
/**
* Creates a new {@link XmlPullParser} that reads XML documents using a
* custom binary wire protocol which benchmarking has shown to be 8.5x
@@ -148,10 +164,17 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlPullParser newBinaryPullParser() {
return new ArtBinaryXmlPullParser();
}
+ /** @hide */
+ public static TypedXmlPullParser newBinaryPullParser$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlPullParser();
+ }
+
/**
* Creates a new {@link XmlPullParser} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
@@ -166,6 +189,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in)
throws IOException {
final byte[] magic = new byte[4];
@@ -198,13 +222,33 @@
return xml;
}
+ /** @hide */
+ public static @NonNull TypedXmlPullParser resolvePullParser$ravenwood(@NonNull InputStream in)
+ throws IOException {
+ // TODO: remove once we're linking against libcore
+ final TypedXmlPullParser xml = new BinaryXmlPullParser();
+ try {
+ xml.setInput(in, StandardCharsets.UTF_8.name());
+ } catch (XmlPullParserException e) {
+ throw new IOException(e);
+ }
+ return xml;
+ }
+
/**
* Creates a new xml serializer.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static XmlSerializer newSerializer() {
return XmlObjectFactory.newXmlSerializer();
}
+ /** @hide */
+ public static XmlSerializer newSerializer$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlSerializer();
+ }
+
/**
* Creates a new {@link XmlSerializer} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
@@ -215,10 +259,17 @@
* @hide
*/
@SuppressWarnings("AndroidFrameworkEfficientXml")
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlSerializer newFastSerializer() {
return XmlUtils.makeTyped(new FastXmlSerializer());
}
+ /** @hide */
+ public static @NonNull TypedXmlSerializer newFastSerializer$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlSerializer();
+ }
+
/**
* Creates a new {@link XmlSerializer} that writes XML documents using a
* custom binary wire protocol which benchmarking has shown to be 4.4x
@@ -227,10 +278,17 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlSerializer newBinarySerializer() {
return new ArtBinaryXmlSerializer();
}
+ /** @hide */
+ public static @NonNull TypedXmlSerializer newBinarySerializer$ravenwood() {
+ // TODO: remove once we're linking against libcore
+ return new BinaryXmlSerializer();
+ }
+
/**
* Creates a new {@link XmlSerializer} which is optimized for use inside the
* system, typically by supporting only a basic set of features.
@@ -245,6 +303,7 @@
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static @NonNull TypedXmlSerializer resolveSerializer(@NonNull OutputStream out)
throws IOException {
final TypedXmlSerializer xml;
@@ -257,6 +316,15 @@
return xml;
}
+ /** @hide */
+ public static @NonNull TypedXmlSerializer resolveSerializer$ravenwood(@NonNull OutputStream out)
+ throws IOException {
+ // TODO: remove once we're linking against libcore
+ final TypedXmlSerializer xml = new BinaryXmlSerializer();
+ xml.setOutput(out, StandardCharsets.UTF_8.name());
+ return xml;
+ }
+
/**
* Copy the first XML document into the second document.
* <p>
diff --git a/core/java/android/util/proto/TEST_MAPPING b/core/java/android/util/proto/TEST_MAPPING
index 5b98741..1261743 100644
--- a/core/java/android/util/proto/TEST_MAPPING
+++ b/core/java/android/util/proto/TEST_MAPPING
@@ -6,5 +6,11 @@
{
"name": "CtsProtoTestCases"
}
+ ],
+ "ravenwood-presubmit": [
+ {
+ "name": "CtsProtoTestCasesRavenwood",
+ "host": true
+ }
]
}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 7e388d4..59ec605 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -157,6 +157,11 @@
public Matrix transform;
/**
+ * The alpha value returned from SurfaceFlinger. This will be ignored if passed as input data.
+ */
+ public float alpha;
+
+ /**
* The input token for the window to which focus should be transferred when this input window
* can be successfully focused. If null, this input window will not transfer its focus to
* any other window.
@@ -199,6 +204,7 @@
}
focusTransferTarget = other.focusTransferTarget;
contentSize = new Size(other.contentSize.getWidth(), other.contentSize.getHeight());
+ alpha = other.alpha;
}
@Override
@@ -212,6 +218,7 @@
.append(", displayId=").append(displayId)
.append(", isClone=").append((inputConfig & InputConfig.CLONE) != 0)
.append(", contentSize=").append(contentSize)
+ .append(", alpha=").append(alpha)
.toString();
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 451a71b..8befe8a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -436,13 +436,16 @@
// Jank due to unknown reasons.
public static final int UNKNOWN = 0x80;
- public JankData(long frameVsyncId, @JankType int jankType) {
+ public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs) {
this.frameVsyncId = frameVsyncId;
this.jankType = jankType;
+ this.frameIntervalNs = frameIntervalNs;
+
}
public final long frameVsyncId;
public final @JankType int jankType;
+ public final long frameIntervalNs;
}
/**
@@ -3280,7 +3283,8 @@
"setCrop", this, sc, "crop=" + crop);
}
if (crop != null) {
- Preconditions.checkArgument(crop.isValid(), "Crop isn't valid.");
+ Preconditions.checkArgument(crop.isValid(), "Crop " + crop
+ + " isn't valid");
nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
crop.left, crop.top, crop.right, crop.bottom);
} else {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0ae14a2..a44a95a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1523,15 +1523,24 @@
if (mSurfaceControl == null) return;
mRTLastReportedPosition.set(left, top, right, bottom);
+ final float postScaleX = mRTLastReportedPosition.width()
+ / (float) mRtSurfaceWidth;
+ final float postScaleY = mRTLastReportedPosition.height()
+ / (float) mRtSurfaceHeight;
onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl,
mRTLastReportedPosition.left /*positionLeft*/,
mRTLastReportedPosition.top /*positionTop*/,
- mRTLastReportedPosition.width()
- / (float) mRtSurfaceWidth /*postScaleX*/,
- mRTLastReportedPosition.height()
- / (float) mRtSurfaceHeight /*postScaleY*/);
+ postScaleX, postScaleY);
- mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
+ mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY),
+ (int) Math.ceil(clipRight / postScaleX),
+ (int) Math.ceil(clipBottom / postScaleY));
+ if (DEBUG_POSITION) {
+ Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
+ + "from scale %f, %f", mRTLastSetCrop.left,
+ mRTLastSetCrop.top, mRTLastSetCrop.right, mRTLastSetCrop.bottom,
+ postScaleX, postScaleY));
+ }
mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop);
if (mRTLastSetCrop.isEmpty()) {
mPositionChangedTransaction.hide(mSurfaceControl);
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ec96167..fb2b8b9 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -88,6 +88,13 @@
private static final int DEFAULT_MULTI_PRESS_TIMEOUT = 300;
/**
+ * Defines the default duration in milliseconds between a key being pressed and its first key
+ * repeat event being generated. Historically, Android used the long press timeout as the
+ * key repeat timeout, so its default value is set to long press timeout's default.
+ */
+ private static final int DEFAULT_KEY_REPEAT_TIMEOUT_MS = DEFAULT_LONG_PRESS_TIMEOUT;
+
+ /**
* Defines the default duration between successive key repeats in milliseconds.
*/
private static final int DEFAULT_KEY_REPEAT_DELAY_MS = 50;
@@ -719,11 +726,8 @@
* @return the time before the first key repeat in milliseconds.
*/
public static int getKeyRepeatTimeout() {
- // Before the key repeat timeout was introduced, some users relied on changing
- // LONG_PRESS_TIMEOUT settings to also change the key repeat timeout. To support
- // this backward compatibility, we'll use the long press timeout as default value.
return AppGlobals.getIntCoreSetting(Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
- getLongPressTimeout());
+ DEFAULT_KEY_REPEAT_TIMEOUT_MS);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 145af2e..f98e1dd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -81,6 +81,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -12029,7 +12031,8 @@
|| motionEventAction == MotionEvent.ACTION_MOVE
|| motionEventAction == MotionEvent.ACTION_UP;
boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
- || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION;
+ || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION
+ || windowType == TYPE_NOTIFICATION_SHADE || windowType == TYPE_STATUS_BAR;
// use toolkitSetFrameRate flag to gate the change
return desiredAction && desiredType && sToolkitSetFrameRateReadOnlyFlagValue;
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index b220c4d..07ae134 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -17,6 +17,7 @@
package android.view.accessibility;
import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -25,6 +26,7 @@
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -2042,6 +2044,9 @@
* @return {@code true} if flash notification works properly.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API)
+ @TestApi
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean startFlashNotificationSequence(@NonNull Context context,
@FlashNotificationReason int reason) {
final IAccessibilityManager service;
@@ -2071,6 +2076,9 @@
* @return {@code true} if flash notification stops properly.
* @hide
*/
+ @FlaggedApi(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API)
+ @TestApi
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean stopFlashNotificationSequence(@NonNull Context context) {
final IAccessibilityManager service;
synchronized (mLock) {
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 5296b99..950fa4b 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -25,6 +25,13 @@
flag {
namespace: "accessibility"
+ name: "flash_notification_system_api"
+ description: "Makes flash notification APIs as system APIs for calling from mainline module"
+ bug: "282821643"
+}
+
+flag {
+ namespace: "accessibility"
name: "force_invert_color"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
bug: "282821643"
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 1e8718c..7486362 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -22,4 +22,12 @@
description: "Feature flag for replacing UserIdInt with UserHandle in some helper IMM functions"
bug: "301713309"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+ name: "concurrent_input_methods"
+ namespace: "input_method"
+ description: "Feature flag for concurrent multi-session IME"
+ bug: "284527000"
+ is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/jank/DisplayRefreshRate.java b/core/java/com/android/internal/jank/DisplayRefreshRate.java
new file mode 100644
index 0000000..354c413
--- /dev/null
+++ b/core/java/com/android/internal/jank/DisplayRefreshRate.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 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.internal.jank;
+
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__UNKNOWN_REFRESH_RATE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__VARIABLE_REFRESH_RATE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_30_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_60_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_90_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_120_HZ;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_240_HZ;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Display refresh rate related functionality.
+ * @hide
+ */
+public class DisplayRefreshRate {
+ public static final int UNKNOWN_REFRESH_RATE =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__UNKNOWN_REFRESH_RATE;
+ public static final int VARIABLE_REFRESH_RATE =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__VARIABLE_REFRESH_RATE;
+ public static final int REFRESH_RATE_30_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_30_HZ;
+ public static final int REFRESH_RATE_60_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_60_HZ;
+ public static final int REFRESH_RATE_90_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_90_HZ;
+ public static final int REFRESH_RATE_120_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_120_HZ;
+ public static final int REFRESH_RATE_240_HZ =
+ UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_REFRESH_RATE__RR_240_HZ;
+
+ /** @hide */
+ @IntDef({
+ UNKNOWN_REFRESH_RATE,
+ VARIABLE_REFRESH_RATE,
+ REFRESH_RATE_30_HZ,
+ REFRESH_RATE_60_HZ,
+ REFRESH_RATE_90_HZ,
+ REFRESH_RATE_120_HZ,
+ REFRESH_RATE_240_HZ,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RefreshRate {
+ }
+
+ private DisplayRefreshRate() {
+ }
+
+ /**
+ * Computes the display refresh rate based off the frame interval.
+ */
+ @RefreshRate
+ public static int getRefreshRate(long frameIntervalNs) {
+ long rate = Math.round(1e9 / frameIntervalNs);
+ if (rate < 50) {
+ return REFRESH_RATE_30_HZ;
+ } else if (rate < 80) {
+ return REFRESH_RATE_60_HZ;
+ } else if (rate < 110) {
+ return REFRESH_RATE_90_HZ;
+ } else if (rate < 180) {
+ return REFRESH_RATE_120_HZ;
+ } else {
+ return REFRESH_RATE_240_HZ;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 506f19f..c83452d 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -24,6 +24,8 @@
import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
+import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE;
+import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
import static com.android.internal.jank.InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT;
@@ -49,6 +51,7 @@
import android.view.WindowCallbacks;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.DisplayRefreshRate.RefreshRate;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
@@ -132,26 +135,30 @@
boolean hwuiCallbackFired;
boolean surfaceControlCallbackFired;
@JankType int jankType;
+ @RefreshRate int refreshRate;
static JankInfo createFromHwuiCallback(long frameVsyncId, long totalDurationNanos,
boolean isFirstFrame) {
- return new JankInfo(frameVsyncId, true, false, JANK_NONE, totalDurationNanos,
- isFirstFrame);
+ return new JankInfo(frameVsyncId, true, false, JANK_NONE, UNKNOWN_REFRESH_RATE,
+ totalDurationNanos, isFirstFrame);
}
static JankInfo createFromSurfaceControlCallback(long frameVsyncId,
- @JankType int jankType) {
- return new JankInfo(frameVsyncId, false, true, jankType, 0, false /* isFirstFrame */);
+ @JankType int jankType, @RefreshRate int refreshRate) {
+ return new JankInfo(
+ frameVsyncId, false, true, jankType, refreshRate, 0, false /* isFirstFrame */);
}
private JankInfo(long frameVsyncId, boolean hwuiCallbackFired,
boolean surfaceControlCallbackFired, @JankType int jankType,
+ @RefreshRate int refreshRate,
long totalDurationNanos, boolean isFirstFrame) {
this.frameVsyncId = frameVsyncId;
this.hwuiCallbackFired = hwuiCallbackFired;
this.surfaceControlCallbackFired = surfaceControlCallbackFired;
- this.totalDurationNanos = totalDurationNanos;
this.jankType = jankType;
+ this.refreshRate = refreshRate;
+ this.totalDurationNanos = totalDurationNanos;
this.isFirstFrame = isFirstFrame;
}
@@ -468,14 +475,16 @@
if (!isInRange(jankStat.frameVsyncId)) {
continue;
}
+ int refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs);
JankInfo info = findJankInfo(jankStat.frameVsyncId);
if (info != null) {
info.surfaceControlCallbackFired = true;
info.jankType = jankStat.jankType;
+ info.refreshRate = refreshRate;
} else {
mJankInfos.put((int) jankStat.frameVsyncId,
JankInfo.createFromSurfaceControlCallback(
- jankStat.frameVsyncId, jankStat.jankType));
+ jankStat.frameVsyncId, jankStat.jankType, refreshRate));
}
}
processJankInfos();
@@ -592,6 +601,7 @@
int missedSfFramesCount = 0;
int maxSuccessiveMissedFramesCount = 0;
int successiveMissedFramesCount = 0;
+ @RefreshRate int refreshRate = UNKNOWN_REFRESH_RATE;
for (int i = 0; i < mJankInfos.size(); i++) {
JankInfo info = mJankInfos.valueAt(i);
@@ -627,6 +637,10 @@
maxSuccessiveMissedFramesCount, successiveMissedFramesCount);
successiveMissedFramesCount = 0;
}
+ if (info.refreshRate != UNKNOWN_REFRESH_RATE && info.refreshRate != refreshRate) {
+ refreshRate = (refreshRate == UNKNOWN_REFRESH_RATE)
+ ? info.refreshRate : VARIABLE_REFRESH_RATE;
+ }
// TODO (b/174755489): Early latch currently gets fired way too often, so we have
// to ignore it for now.
if (!mSurfaceOnly && !info.hwuiCallbackFired) {
@@ -669,6 +683,7 @@
mStatsLog.write(
FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
mDisplayId,
+ refreshRate,
mSession.getStatsdInteractionType(),
totalFramesCount,
missedFramesCount,
@@ -866,10 +881,10 @@
}
/** {@see FrameworkStatsLog#write) */
- public void write(int code, int displayId,
+ public void write(int code, int displayId, @RefreshRate int refreshRate,
int arg1, long arg2, long arg3, long arg4, long arg5, long arg6, long arg7) {
FrameworkStatsLog.write(code, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- mDisplayResolutionTracker.getResolution(displayId));
+ mDisplayResolutionTracker.getResolution(displayId), refreshRate);
}
}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 0947ec1..f62094d 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -618,4 +618,14 @@
public int describeContents() {
return 0;
}
+
+ @Override
+ public VpnProfile clone() {
+ try {
+ return (VpnProfile) super.clone();
+ } catch (CloneNotSupportedException e) {
+ Log.wtf(TAG, e);
+ return null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/util/ArtFastDataInput.java b/core/java/com/android/internal/util/ArtFastDataInput.java
index 3e8916c..768ea82 100644
--- a/core/java/com/android/internal/util/ArtFastDataInput.java
+++ b/core/java/com/android/internal/util/ArtFastDataInput.java
@@ -21,6 +21,8 @@
import com.android.modules.utils.FastDataInput;
+import dalvik.system.VMRuntime;
+
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
@@ -35,13 +37,14 @@
*/
public class ArtFastDataInput extends FastDataInput {
private static AtomicReference<ArtFastDataInput> sInCache = new AtomicReference<>();
+ private static VMRuntime sRuntime = VMRuntime.getRuntime();
private final long mBufferPtr;
public ArtFastDataInput(@NonNull InputStream in, int bufferSize) {
super(in, bufferSize);
- mBufferPtr = mRuntime.addressOf(mBuffer);
+ mBufferPtr = sRuntime.addressOf(mBuffer);
}
/**
@@ -66,6 +69,7 @@
* Release a {@link ArtFastDataInput} to potentially be recycled. You must not
* interact with the object after releasing it.
*/
+ @Override
public void release() {
super.release();
@@ -76,6 +80,11 @@
}
@Override
+ public byte[] newByteArray(int bufferSize) {
+ return (byte[]) sRuntime.newNonMovableArray(byte.class, bufferSize);
+ }
+
+ @Override
public String readUTF() throws IOException {
// Attempt to read directly from buffer space if there's enough room,
// otherwise fall back to chunking into place
@@ -86,9 +95,9 @@
mBufferPos += len;
return res;
} else {
- final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
+ final byte[] tmp = (byte[]) sRuntime.newNonMovableArray(byte.class, len + 1);
readFully(tmp, 0, len);
- return CharsetUtils.fromModifiedUtf8Bytes(mRuntime.addressOf(tmp), 0, len);
+ return CharsetUtils.fromModifiedUtf8Bytes(sRuntime.addressOf(tmp), 0, len);
}
}
}
diff --git a/core/java/com/android/internal/util/ArtFastDataOutput.java b/core/java/com/android/internal/util/ArtFastDataOutput.java
index ac595b6..360ddb8 100644
--- a/core/java/com/android/internal/util/ArtFastDataOutput.java
+++ b/core/java/com/android/internal/util/ArtFastDataOutput.java
@@ -21,6 +21,8 @@
import com.android.modules.utils.FastDataOutput;
+import dalvik.system.VMRuntime;
+
import java.io.DataOutput;
import java.io.IOException;
import java.io.OutputStream;
@@ -35,13 +37,14 @@
*/
public class ArtFastDataOutput extends FastDataOutput {
private static AtomicReference<ArtFastDataOutput> sOutCache = new AtomicReference<>();
+ private static VMRuntime sRuntime = VMRuntime.getRuntime();
private final long mBufferPtr;
public ArtFastDataOutput(@NonNull OutputStream out, int bufferSize) {
super(out, bufferSize);
- mBufferPtr = mRuntime.addressOf(mBuffer);
+ mBufferPtr = sRuntime.addressOf(mBuffer);
}
/**
@@ -73,6 +76,11 @@
}
@Override
+ public byte[] newByteArray(int bufferSize) {
+ return (byte[]) sRuntime.newNonMovableArray(byte.class, bufferSize);
+ }
+
+ @Override
public void writeUTF(String s) throws IOException {
// Attempt to write directly to buffer space if there's enough room,
// otherwise fall back to chunking into place
@@ -94,8 +102,8 @@
// Negative value indicates buffer was too small and we need to
// allocate a temporary buffer for encoding
len = -len;
- final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1);
- CharsetUtils.toModifiedUtf8Bytes(s, mRuntime.addressOf(tmp), 0, tmp.length);
+ final byte[] tmp = (byte[]) sRuntime.newNonMovableArray(byte.class, len + 1);
+ CharsetUtils.toModifiedUtf8Bytes(s, sRuntime.addressOf(tmp), 0, tmp.length);
writeShort(len);
write(tmp, 0, len);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a3e0016..28fd2b4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1936,7 +1936,8 @@
* If the user is not secured, ie doesn't have an LSKF, then decrypt the user's synthetic
* password and use it to unlock various cryptographic keys associated with the user. This
* primarily includes unlocking the user's credential-encrypted (CE) storage. It also includes
- * deriving or decrypting the vendor auth secret and sending it to the AuthSecret HAL.
+ * unlocking the user's Keystore super keys, and deriving or decrypting the vendor auth secret
+ * and sending it to the AuthSecret HAL in order to unlock Secure Element firmware updates.
* <p>
* These tasks would normally be done when the LSKF is verified. This method is where these
* tasks are done when the user doesn't have an LSKF. It's called when the user is started.
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 139b88b..61e017d 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,7 @@
}
@Override
- public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos,
+ public void interfaceClassDataActivityChanged(int label, boolean active, long tsNanos,
int uid) {
// default no-op
}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index c3d21a4..ae23942 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -74,6 +74,7 @@
jfieldID transform;
jfieldID windowToken;
jfieldID focusTransferTarget;
+ jfieldID alpha;
} gInputWindowHandleClassInfo;
static struct {
@@ -325,6 +326,8 @@
env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken,
javaObjectForIBinder(env, windowInfo.windowToken));
+ env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.alpha, windowInfo.alpha);
+
return inputWindowHandle;
}
@@ -446,6 +449,8 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl, clazz,
"touchableRegionSurfaceControl", "Ljava/lang/ref/WeakReference;");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.alpha, clazz, "alpha", "F");
+
jclass surfaceControlClazz;
FIND_CLASS(surfaceControlClazz, "android/view/SurfaceControl");
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 8fa179b..f9d00ed 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -212,14 +212,11 @@
return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
}
- size_t bytesPerSample = audio_bytes_per_sample(format);
-
if (buffSizeInBytes == 0) {
ALOGE("Error creating AudioRecord: frameCount is 0.");
return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
}
- size_t frameSize = channelCount * bytesPerSample;
- size_t frameCount = buffSizeInBytes / frameSize;
+ size_t frameCount = buffSizeInBytes / audio_bytes_per_frame(channelCount, format);
// create an uninitialized AudioRecord object
Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a800e6e..db42246 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1951,8 +1951,9 @@
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (size_t i = 0; i < jankData.size(); i++) {
- jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
- gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
+ jobject jJankData = env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
+ jankData[i].frameVsyncId, jankData[i].jankType,
+ jankData[i].frameIntervalNs);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
env->DeleteLocalRef(jJankData);
}
@@ -2523,8 +2524,7 @@
jclass jankDataClazz =
FindClassOrDie(env, "android/view/SurfaceControl$JankData");
gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
- gJankDataClassInfo.ctor =
- GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JI)V");
+ gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JIJ)V");
jclass onJankDataListenerClazz =
FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener");
gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 76ae3e0..926e0fa 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2372,7 +2372,7 @@
them from running without explicit user action.
-->
<permission android:name="android.permission.QUARANTINE_APPS"
- android:protectionLevel="internal|verifier" />
+ android:protectionLevel="signature|verifier" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 39d958c..8d80af4 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -243,10 +243,6 @@
<bool name="allow_clear_initial_attach_data_profile">false</bool>
<java-symbol type="bool" name="allow_clear_initial_attach_data_profile" />
- <!-- Boolean indicating whether TelephonyAnalytics module is active or not. -->
- <bool name="telephony_analytics_switch">true</bool>
- <java-symbol type="bool" name="telephony_analytics_switch" />
-
<!-- Whether to enable modem on boot if behavior is not defined -->
<bool name="config_enable_cellular_on_boot_default">true</bool>
<java-symbol type="bool" name="config_enable_cellular_on_boot_default" />
diff --git a/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java b/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
new file mode 100644
index 0000000..e19c4b1
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/LauncherActivityInfoTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link android.content.pm.LauncherActivityInfo}
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LauncherActivityInfoTest {
+
+ @Test
+ public void testTrimStart() {
+ // Invisible case
+ assertThat(LauncherActivityInfo.trimStart("\u0009").toString()).isEmpty();
+ // It is not supported in the system font
+ assertThat(LauncherActivityInfo.trimStart("\u0FE1").toString()).isEmpty();
+ // Surrogates case
+ assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36").toString())
+ .isEqualTo("\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimStart("\u0009\u0FE1\uD83E\uDD36A").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trimStart("\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
+ assertThat(LauncherActivityInfo.trimStart("A\uD83E\uDD36\u0009\u0FE1A").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
+ assertThat(LauncherActivityInfo.trimStart(
+ "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimStart(
+ "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A\u0009\u0FE1");
+ }
+
+ @Test
+ public void testTrimEnd() {
+ // Invisible case
+ assertThat(LauncherActivityInfo.trimEnd("\u0009").toString()).isEmpty();
+ // It is not supported in the system font
+ assertThat(LauncherActivityInfo.trimEnd("\u0FE1").toString()).isEmpty();
+ // Surrogates case
+ assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36").toString())
+ .isEqualTo("\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimEnd("\u0009\u0FE1\uD83E\uDD36A").toString())
+ .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trimEnd("\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trimEnd("A\uD83E\uDD36\u0009\u0FE1A").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
+ assertThat(LauncherActivityInfo.trimEnd(
+ "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trimEnd(
+ "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\u0009\u0FE1\uD83E\uDD36A");
+ }
+
+ @Test
+ public void testTrim() {
+ // Invisible case
+ assertThat(LauncherActivityInfo.trim("\u0009").toString()).isEmpty();
+ // It is not supported in the system font
+ assertThat(LauncherActivityInfo.trim("\u0FE1").toString()).isEmpty();
+ // Surrogates case
+ assertThat(LauncherActivityInfo.trim("\uD83E\uDD36").toString())
+ .isEqualTo("\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trim("\u0009\u0FE1\uD83E\uDD36A").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trim("\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ assertThat(LauncherActivityInfo.trim("A\uD83E\uDD36\u0009\u0FE1A").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A");
+ assertThat(LauncherActivityInfo.trim(
+ "A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36").toString())
+ .isEqualTo("A\uD83E\uDD36\u0009\u0FE1A\uD83E\uDD36");
+ assertThat(LauncherActivityInfo.trim(
+ "\u0009\u0FE1\uD83E\uDD36A\u0009\u0FE1").toString())
+ .isEqualTo("\uD83E\uDD36A");
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java b/core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java
new file mode 100644
index 0000000..725885a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/jank/DisplayRefreshRateTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 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.internal.jank;
+
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_120_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_240_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_30_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_60_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.REFRESH_RATE_90_HZ;
+import static com.android.internal.jank.DisplayRefreshRate.getRefreshRate;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class DisplayRefreshRateTest {
+ @Test
+ public void testRefreshRateMapping() {
+ assertThat(getRefreshRate((long) 1e9 / 30)).isEqualTo(REFRESH_RATE_30_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 60)).isEqualTo(REFRESH_RATE_60_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 90)).isEqualTo(REFRESH_RATE_90_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 120)).isEqualTo(REFRESH_RATE_120_HZ);
+ assertThat(getRefreshRate((long) 1e9 / 240)).isEqualTo(REFRESH_RATE_240_HZ);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index dbbd705..1b9717a 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -70,6 +70,8 @@
@SmallTest
public class FrameTrackerTest {
private static final String CUJ_POSTFIX = "";
+ private static final long FRAME_TIME_60Hz = (long) 1e9 / 60;
+
private ViewAttachTestActivity mActivity;
@Rule
@@ -170,6 +172,7 @@
verify(tracker, never()).triggerPerfetto();
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -207,6 +210,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -244,6 +248,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -281,6 +286,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -321,6 +327,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -363,6 +370,7 @@
verify(tracker, never()).triggerPerfetto();
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -491,6 +499,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(2L) /* totalFrames */,
eq(1L) /* missedFrames */,
@@ -528,6 +537,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -565,6 +575,7 @@
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(2L) /* totalFrames */,
eq(0L) /* missedFrames */,
@@ -596,6 +607,7 @@
verify(tracker).triggerPerfetto();
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
eq(42), /* displayId */
+ eq(DisplayRefreshRate.REFRESH_RATE_60_HZ),
eq(CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_WALLPAPER_TRANSITION]),
eq(6L) /* totalFrames */,
eq(5L) /* missedFrames */,
@@ -648,7 +660,7 @@
final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
doNothing().when(tracker).postCallback(captor.capture());
mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
- new JankData(vsyncId, jankType)
+ new JankData(vsyncId, jankType, FRAME_TIME_60Hz)
});
captor.getValue().run();
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b065611..f19acbe 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -811,6 +811,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1325565952": {
+ "message": "Attempted to get home support flag of a display that does not exist: %d",
+ "level": "WARN",
+ "group": "WM_ERROR",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-1323783276": {
"message": "performEnableScreen: bootFinished() failed.",
"level": "WARN",
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 51b720d..f9347ee 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -324,7 +324,7 @@
# key 365 "KEY_EPG"
key 366 DVR
# key 367 "KEY_MHP"
-# key 368 "KEY_LANGUAGE"
+key 368 LANGUAGE_SWITCH
# key 369 "KEY_TITLE"
key 370 CAPTIONS
# key 371 "KEY_ANGLE"
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index b7ea04f..2beb434 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -51,7 +51,7 @@
* @return 0 if successful or a {@code ResponseCode}
* @hide
*/
- public static int onUserAdded(@NonNull int userId) {
+ public static int onUserAdded(int userId) {
StrictMode.noteDiskWrite();
try {
getService().onUserAdded(userId);
@@ -66,6 +66,30 @@
}
/**
+ * Tells Keystore to create a user's super keys and store them encrypted by the given secret.
+ *
+ * @param userId - Android user id of the user
+ * @param password - a secret derived from the user's synthetic password
+ * @param allowExisting - true if the keys already existing should not be considered an error
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int initUserSuperKeys(int userId, @NonNull byte[] password,
+ boolean allowExisting) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().initUserSuperKeys(userId, password, allowExisting);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "initUserSuperKeys failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
* Informs Keystore 2.0 about removing a user
*
* @param userId - Android user id of the user being removed
@@ -110,6 +134,28 @@
}
/**
+ * Tells Keystore that a user's LSKF is being removed, ie the user's lock screen is changing to
+ * Swipe or None. Keystore uses this notification to delete the user's auth-bound keys.
+ *
+ * @param userId - Android user id of the user
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int onUserLskfRemoved(int userId) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().onUserLskfRemoved(userId);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "onUserLskfRemoved failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
* Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to
* be cleared.
*/
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 29bdd5c..c366ccd 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -35,3 +35,10 @@
description: "Enables taskbar / navbar unification"
bug: "309671494"
}
+
+flag {
+ name: "enable_pip_ui_state_on_entering"
+ namespace: "multitasking"
+ description: "Enables PiP UI state callback on entering"
+ bug: "303718131"
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index ab61a48..5143d41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -115,6 +115,19 @@
b.setParent(sc);
}
+ /**
+ * Re-parents the provided surface to the leash of the provided display.
+ *
+ * @param displayId the display area to reparent to.
+ * @param sc the surface to be reparented.
+ * @param t a {@link SurfaceControl.Transaction} in which to reparent.
+ */
+ public void reparentToDisplayArea(int displayId, SurfaceControl sc,
+ SurfaceControl.Transaction t) {
+ final SurfaceControl displayAreaLeash = mLeashes.get(displayId);
+ t.reparent(sc, displayAreaLeash);
+ }
+
public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) {
final SurfaceControl sc = mLeashes.get(displayId);
if (sc == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 6cd1324..efa5a1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -21,6 +21,7 @@
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -253,7 +254,8 @@
private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
- final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE,
+ final int type = getTransitionTypeFromInfo(info);
+ final Animation a = loadAttributeAnimation(type, info, change, WALLPAPER_TRANSITION_NONE,
mTransitionAnimation, false);
return a != null && a.getShowBackdrop();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 47769a8..71bf487 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -59,6 +59,7 @@
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
@@ -498,6 +499,7 @@
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
+ DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
LaunchAdjacentController launchAdjacentController,
RecentsTransitionHandler recentsTransitionHandler,
@@ -506,8 +508,19 @@
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
- toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository,
- launchAdjacentController, recentsTransitionHandler, mainExecutor);
+ toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler,
+ desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler,
+ mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static DragToDesktopTransitionHandler provideDragToDesktopTransitionHandler(
+ Context context,
+ Transitions transitions,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ return new DragToDesktopTransitionHandler(context, transitions,
+ rootTaskDisplayAreaOrganizer);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 8e12991..4a9ea6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -59,6 +59,7 @@
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
@@ -92,6 +93,7 @@
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
private val toggleResizeDesktopTaskTransitionHandler:
ToggleResizeDesktopTaskTransitionHandler,
+ private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
private val desktopModeTaskRepository: DesktopModeTaskRepository,
private val launchAdjacentController: LaunchAdjacentController,
private val recentsTransitionHandler: RecentsTransitionHandler,
@@ -110,6 +112,20 @@
launchAdjacentController.launchAdjacentEnabled = !hasVisibleFreeformTasks
}
}
+ private val dragToDesktopStateListener = object : DragToDesktopStateListener {
+ override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) {
+ removeVisualIndicator(tx)
+ }
+
+ override fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction) {
+ removeVisualIndicator(tx)
+ }
+
+ private fun removeVisualIndicator(tx: SurfaceControl.Transaction) {
+ visualIndicator?.releaseVisualIndicator(tx)
+ visualIndicator = null
+ }
+ }
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
@@ -122,9 +138,7 @@
)
private var recentsAnimationRunning = false
-
- // This is public to avoid cyclic dependency; it is set by SplitScreenController
- lateinit var splitScreenController: SplitScreenController
+ private lateinit var splitScreenController: SplitScreenController
init {
desktopMode = DesktopModeImpl()
@@ -143,7 +157,7 @@
)
transitions.addHandler(this)
desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
-
+ dragToDesktopTransitionHandler.setDragToDesktopStateListener(dragToDesktopStateListener)
recentsTransitionHandler.addTransitionStateListener(
object : RecentsTransitionStateListener {
override fun onAnimationStateChanged(running: Boolean) {
@@ -158,6 +172,12 @@
)
}
+ /** Setter needed to avoid cyclic dependency. */
+ fun setSplitScreenController(controller: SplitScreenController) {
+ splitScreenController = controller
+ dragToDesktopTransitionHandler.setSplitScreenController(controller)
+ }
+
/** Show all tasks, that are part of the desktop, on top of launcher */
fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
@@ -248,56 +268,43 @@
}
/**
- * The first part of the animated move to desktop transition. Applies the changes to move task
- * to desktop mode and sets the taskBounds to the passed in bounds, startBounds. This is
- * followed with a call to {@link finishMoveToDesktop} or {@link cancelMoveToDesktop}.
+ * The first part of the animated drag to desktop transition. This is
+ * followed with a call to [finalizeDragToDesktop] or [cancelDragToDesktop].
*/
- fun startMoveToDesktop(
+ fun startDragToDesktop(
taskInfo: RunningTaskInfo,
- startBounds: Rect,
- dragToDesktopValueAnimator: MoveToDesktopAnimator
+ dragToDesktopValueAnimator: MoveToDesktopAnimator,
+ windowDecor: DesktopModeWindowDecoration
) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: startMoveToDesktop taskId=%d",
- taskInfo.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: startDragToDesktop taskId=%d",
+ taskInfo.taskId
+ )
+ dragToDesktopTransitionHandler.startDragToDesktopTransition(
+ taskInfo.taskId,
+ dragToDesktopValueAnimator,
+ windowDecor
+ )
+ }
+
+ /**
+ * The second part of the animated drag to desktop transition, called after
+ * [startDragToDesktop].
+ */
+ private fun finalizeDragToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: finalizeDragToDesktop taskId=%d",
+ taskInfo.taskId
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
moveHomeTaskToFront(wct)
- addMoveToDesktopChanges(wct, taskInfo)
- wct.setBounds(taskInfo.token, startBounds)
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.startMoveToDesktop(wct, dragToDesktopValueAnimator,
- mOnAnimationFinishedCallback)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
- }
- }
-
- /**
- * The second part of the animated move to desktop transition, called after
- * {@link startMoveToDesktop}. Brings apps to front and sets freeform task bounds.
- */
- private fun finalizeMoveToDesktop(taskInfo: RunningTaskInfo, freeformBounds: Rect) {
- KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: finalizeMoveToDesktop taskId=%d",
- taskInfo.taskId
- )
- val wct = WindowContainerTransaction()
bringDesktopAppsToFront(taskInfo.displayId, wct)
addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, freeformBounds)
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct,
- mOnAnimationFinishedCallback)
- } else {
- shellTaskOrganizer.applyTransaction(wct)
- releaseVisualIndicator()
- }
+ dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
}
/**
@@ -353,40 +360,40 @@
}
private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) {
- if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
- splitScreenController.prepareExitSplitScreen(wct,
- splitScreenController.getStageOfTask(taskInfo.taskId), EXIT_REASON_ENTER_DESKTOP)
+ if (splitScreenController.isTaskInSplitScreen(taskInfo.taskId)) {
+ splitScreenController.prepareExitSplitScreen(
+ wct,
+ splitScreenController.getStageOfTask(taskInfo.taskId),
+ EXIT_REASON_ENTER_DESKTOP
+ )
+ getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
+ wct.removeTask(otherTaskInfo.token)
+ }
}
}
+ private fun getOtherSplitTask(taskId: Int): RunningTaskInfo? {
+ val remainingTaskPosition: Int =
+ if (splitScreenController.getSplitPosition(taskId)
+ == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+ SPLIT_POSITION_TOP_OR_LEFT
+ } else {
+ SPLIT_POSITION_BOTTOM_OR_RIGHT
+ }
+ return splitScreenController.getTaskInfo(remainingTaskPosition)
+ }
+
/**
- * The second part of the animated move to desktop transition, called after
- * {@link startMoveToDesktop}. Move a task to fullscreen after being dragged from fullscreen
- * and released back into status bar area.
+ * The second part of the animated drag to desktop transition, called after
+ * [startDragToDesktop].
*/
- fun cancelMoveToDesktop(task: RunningTaskInfo, moveToDesktopAnimator: MoveToDesktopAnimator) {
+ fun cancelDragToDesktop(task: RunningTaskInfo) {
KtProtoLog.v(
WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: cancelMoveToDesktop taskId=%d",
+ "DesktopTasksController: cancelDragToDesktop taskId=%d",
task.taskId
)
- val wct = WindowContainerTransaction()
- wct.setBounds(task.token, Rect())
-
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct,
- moveToDesktopAnimator) { t ->
- val callbackWCT = WindowContainerTransaction()
- visualIndicator?.releaseVisualIndicator(t)
- visualIndicator = null
- addMoveToFullscreenChanges(callbackWCT, task)
- transitions.startTransition(TRANSIT_CHANGE, callbackWCT, null /* handler */)
- }
- } else {
- addMoveToFullscreenChanges(wct, task)
- shellTaskOrganizer.applyTransaction(wct)
- releaseVisualIndicator()
- }
+ dragToDesktopTransitionHandler.cancelDragToDesktopTransition()
}
private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) {
@@ -966,6 +973,11 @@
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR)
+ // TODO(b/301106941): don't show the indicator until the drag-to-desktop animation has
+ // started, or it'll be visible too early on top of the task surface, especially in
+ // the cancel-early case. Also because it shouldn't even be shown in the cancel-early
+ // case since its dismissal is tied to the cancel animation end, which doesn't even run
+ // in cancel-early.
visualIndicator?.createIndicatorWithAnimatedBounds()
}
val indicator = visualIndicator ?: return
@@ -988,7 +1000,7 @@
taskInfo: RunningTaskInfo,
freeformBounds: Rect
) {
- finalizeMoveToDesktop(taskInfo, freeformBounds)
+ finalizeDragToDesktop(taskInfo, freeformBounds)
}
private fun getStatusBarHeight(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
new file mode 100644
index 0000000..75d27d9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -0,0 +1,543 @@
+package com.android.wm.shell.desktopmode
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.RectEvaluator
+import android.animation.ValueAnimator
+import android.app.ActivityOptions
+import android.app.ActivityOptions.SourceInfo
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
+import android.app.PendingIntent.FLAG_MUTABLE
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.FILL_IN_COMPONENT
+import android.graphics.Rect
+import android.os.IBinder
+import android.os.SystemClock
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TransitionHandler
+import com.android.wm.shell.util.TransitionUtil
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator.Companion.DRAG_FREEFORM_SCALE
+import java.util.function.Supplier
+
+/**
+ * Handles the transition to enter desktop from fullscreen by dragging on the handle bar. It also
+ * handles the cancellation case where the task is dragged back to the status bar area in the same
+ * gesture.
+ */
+class DragToDesktopTransitionHandler(
+ private val context: Context,
+ private val transitions: Transitions,
+ private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val transactionSupplier: Supplier<SurfaceControl.Transaction>
+) : TransitionHandler {
+
+ constructor(
+ context: Context,
+ transitions: Transitions,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ ) : this(
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ Supplier { SurfaceControl.Transaction() }
+ )
+
+ private val rectEvaluator = RectEvaluator(Rect())
+ private val launchHomeIntent = Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+
+ private var dragToDesktopStateListener: DragToDesktopStateListener? = null
+ private var splitScreenController: SplitScreenController? = null
+ private var transitionState: TransitionState? = null
+
+ /** Sets a listener to receive callback about events during the transition animation. */
+ fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
+ dragToDesktopStateListener = listener
+ }
+
+ /** Setter needed to avoid cyclic dependency. */
+ fun setSplitScreenController(controller: SplitScreenController) {
+ splitScreenController = controller
+ }
+
+ /**
+ * Starts a transition that performs a transient launch of Home so that Home is brought to the
+ * front while still keeping the currently focused task that is being dragged resumed. This
+ * allows the animation handler to reorder the task to the front and to scale it with the
+ * gesture into the desktop area with the Home and wallpaper behind it.
+ *
+ * Note that the transition handler for this transition doesn't call the finish callback until
+ * after one of the "end" or "cancel" transitions is merged into this transition.
+ */
+ fun startDragToDesktopTransition(
+ taskId: Int,
+ dragToDesktopAnimator: MoveToDesktopAnimator,
+ windowDecoration: DesktopModeWindowDecoration
+ ) {
+ if (transitionState != null) {
+ error("A drag to desktop is already in progress")
+ }
+
+ val options = ActivityOptions.makeBasic().apply {
+ setTransientLaunch()
+ setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ context,
+ 0 /* requestCode */,
+ launchHomeIntent,
+ FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT
+ )
+ val wct = WindowContainerTransaction()
+ wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
+ val startTransitionToken = transitions
+ .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
+
+ transitionState = if (isSplitTask(taskId)) {
+ TransitionState.FromSplit(
+ draggedTaskId = taskId,
+ dragAnimator = dragToDesktopAnimator,
+ windowDecoration = windowDecoration,
+ startTransitionToken = startTransitionToken
+ )
+ } else {
+ TransitionState.FromFullscreen(
+ draggedTaskId = taskId,
+ dragAnimator = dragToDesktopAnimator,
+ windowDecoration = windowDecoration,
+ startTransitionToken = startTransitionToken
+ )
+ }
+ }
+
+ /**
+ * Starts a transition that "finishes" the drag to desktop gesture. This transition is intended
+ * to merge into the "start" transition and is the one that actually applies the bounds and
+ * windowing mode changes to the dragged task. This is called when the dragged task is released
+ * inside the desktop drop zone.
+ */
+ fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
+ transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
+ }
+
+ /**
+ * Starts a transition that "cancels" the drag to desktop gesture. This transition is intended
+ * to merge into the "start" transition and it restores the transient state that was used to
+ * launch the Home task over the dragged task. This is called when the dragged task is released
+ * outside the desktop drop zone and is instead dropped back into the status bar region that
+ * means the user wants to remain in their current windowing mode.
+ */
+ fun cancelDragToDesktopTransition() {
+ val state = requireTransitionState()
+ state.cancelled = true
+ if (state.draggedTaskChange != null) {
+ // Regular case, transient launch of Home happened as is waiting for the cancel
+ // transient to start and merge. Animate the cancellation (scale back to original
+ // bounds) first before actually starting the cancel transition so that the wallpaper
+ // is visible behind the animating task.
+ startCancelAnimation()
+ } else {
+ // There's no dragged task, this can happen when the "cancel" happened too quickly
+ // before the "start" transition is even ready (like on a fling gesture). The
+ // "shrink" animation didn't even start, so there's no need to animate the "cancel".
+ // We also don't want to start the cancel transition yet since we don't have
+ // enough info to restore the order. We'll check for the cancelled state flag when
+ // the "start" animation is ready and cancel from #startAnimation instead.
+ }
+ }
+
+ override fun startAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: Transitions.TransitionFinishCallback
+ ): Boolean {
+ val state = requireTransitionState()
+
+ val isStartDragToDesktop = info.type == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP &&
+ transition == state.startTransitionToken
+ if (!isStartDragToDesktop) {
+ return false
+ }
+
+ // Layering: non-wallpaper, non-home tasks excluding the dragged task go at the bottom,
+ // then Home on top of that, wallpaper on top of that and finally the dragged task on top
+ // of everything.
+ val appLayers = info.changes.size
+ val homeLayers = info.changes.size * 2
+ val wallpaperLayers = info.changes.size * 3
+ val dragLayer = wallpaperLayers
+ val leafTaskFilter = TransitionUtil.LeafTaskFilter()
+ info.changes.withIndex().forEach { (i, change) ->
+ if (TransitionUtil.isWallpaper(change)) {
+ val layer = wallpaperLayers - i
+ startTransaction.apply {
+ setLayer(change.leash, layer)
+ show(change.leash)
+ }
+ } else if (isHomeChange(change)) {
+ state.homeToken = change.container
+ val layer = homeLayers - i
+ startTransaction.apply {
+ setLayer(change.leash, layer)
+ show(change.leash)
+ }
+ } else if (TransitionInfo.isIndependent(change, info)) {
+ // Root.
+ when (state) {
+ is TransitionState.FromSplit -> {
+ state.splitRootChange = change
+ val layer = if (!state.cancelled) {
+ // Normal case, split root goes to the bottom behind everything else.
+ appLayers - i
+ } else {
+ // Cancel-early case, pretend nothing happened so split root stays top.
+ dragLayer
+ }
+ startTransaction.apply {
+ setLayer(change.leash, layer)
+ show(change.leash)
+ }
+ }
+ is TransitionState.FromFullscreen -> {
+ if (change.taskInfo?.taskId == state.draggedTaskId) {
+ state.draggedTaskChange = change
+ val bounds = change.endAbsBounds
+ startTransaction.apply {
+ setLayer(change.leash, dragLayer)
+ setWindowCrop(change.leash, bounds.width(), bounds.height())
+ show(change.leash)
+ }
+ } else {
+ throw IllegalStateException("Expected root to be dragged task")
+ }
+ }
+ }
+ } else if (leafTaskFilter.test(change)) {
+ // When dragging one of the split tasks, the dragged leaf needs to be re-parented
+ // so that it can be layered separately from the rest of the split root/stages.
+ // The split root including the other split side was layered behind the wallpaper
+ // and home while the dragged split needs to be layered in front of them.
+ // Do not do this in the cancel-early case though, since in that case nothing should
+ // happen on screen so the layering will remain the same as if no transition
+ // occurred.
+ if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) {
+ state.draggedTaskChange = change
+ taskDisplayAreaOrganizer.reparentToDisplayArea(
+ change.endDisplayId, change.leash, startTransaction)
+ val bounds = change.endAbsBounds
+ startTransaction.apply {
+ setLayer(change.leash, dragLayer)
+ setWindowCrop(change.leash, bounds.width(), bounds.height())
+ show(change.leash)
+ }
+ }
+ }
+ }
+ state.startTransitionFinishCb = finishCallback
+ state.startTransitionFinishTransaction = finishTransaction
+ startTransaction.apply()
+
+ if (!state.cancelled) {
+ // Normal case, start animation to scale down the dragged task. It'll also be moved to
+ // follow the finger and when released we'll start the next phase/transition.
+ state.dragAnimator.startAnimation()
+ } else {
+ // Cancel-early case, the state was flagged was cancelled already, which means the
+ // gesture ended in the cancel region. This can happen even before the start transition
+ // is ready/animate here when cancelling quickly like with a fling. There's no point
+ // in starting the scale down animation that we would scale up anyway, so just jump
+ // directly into starting the cancel transition to restore WM order. Surfaces should
+ // not move as if no transition happened.
+ startCancelDragToDesktopTransition()
+ }
+ return true
+ }
+
+ override fun mergeAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ mergeTarget: IBinder,
+ finishCallback: Transitions.TransitionFinishCallback
+ ) {
+ val state = requireTransitionState()
+ val isCancelTransition = info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
+ transition == state.cancelTransitionToken &&
+ mergeTarget == state.startTransitionToken
+ val isEndTransition = info.type == TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP &&
+ mergeTarget == state.startTransitionToken
+
+ val startTransactionFinishT = state.startTransitionFinishTransaction
+ ?: error("Start transition expected to be waiting for merge but wasn't")
+ val startTransitionFinishCb = state.startTransitionFinishCb
+ ?: error("Start transition expected to be waiting for merge but wasn't")
+ if (isEndTransition) {
+ info.changes.withIndex().forEach { (i, change) ->
+ if (change.mode == TRANSIT_CLOSE) {
+ t.hide(change.leash)
+ startTransactionFinishT.hide(change.leash)
+ } else if (change.taskInfo?.taskId == state.draggedTaskId) {
+ t.show(change.leash)
+ startTransactionFinishT.show(change.leash)
+ state.draggedTaskChange = change
+ } else if (change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM) {
+ // Other freeform tasks that are being restored go behind the dragged task.
+ val draggedTaskLeash = state.draggedTaskChange?.leash
+ ?: error("Expected dragged leash to be non-null")
+ t.setRelativeLayer(change.leash, draggedTaskLeash, -i)
+ startTransactionFinishT.setRelativeLayer(change.leash, draggedTaskLeash, -i)
+ }
+ }
+
+ val draggedTaskChange = state.draggedTaskChange
+ ?: throw IllegalStateException("Expected non-null change of dragged task")
+ val draggedTaskLeash = draggedTaskChange.leash
+ val startBounds = draggedTaskChange.startAbsBounds
+ val endBounds = draggedTaskChange.endAbsBounds
+
+ // TODO(b/301106941): Instead of forcing-finishing the animation that scales the
+ // surface down and then starting another that scales it back up to the final size,
+ // blend the two animations.
+ state.dragAnimator.endAnimator()
+ // Using [DRAG_FREEFORM_SCALE] to calculate animated width/height is possible because
+ // it is known that the animation scale is finished because the animation was
+ // force-ended above. This won't be true when the two animations are blended.
+ val animStartWidth = (startBounds.width() * DRAG_FREEFORM_SCALE).toInt()
+ val animStartHeight = (startBounds.height() * DRAG_FREEFORM_SCALE).toInt()
+ // Using end bounds here to find the left/top also assumes the center animation has
+ // finished and the surface is placed exactly in the center of the screen which matches
+ // the end/default bounds of the now freeform task.
+ val animStartLeft = endBounds.centerX() - (animStartWidth / 2)
+ val animStartTop = endBounds.centerY() - (animStartHeight / 2)
+ val animStartBounds = Rect(
+ animStartLeft,
+ animStartTop,
+ animStartLeft + animStartWidth,
+ animStartTop + animStartHeight
+ )
+
+
+ dragToDesktopStateListener?.onCommitToDesktopAnimationStart(t)
+ t.apply {
+ setScale(draggedTaskLeash, 1f, 1f)
+ setPosition(
+ draggedTaskLeash,
+ animStartBounds.left.toFloat(),
+ animStartBounds.top.toFloat()
+ )
+ setWindowCrop(
+ draggedTaskLeash,
+ animStartBounds.width(),
+ animStartBounds.height()
+ )
+ }
+ // Accept the merge by applying the merging transaction (applied by #showResizeVeil)
+ // and finish callback. Show the veil and position the task at the first frame before
+ // starting the final animation.
+ state.windowDecoration.showResizeVeil(t, animStartBounds)
+ finishCallback.onTransitionFinished(null /* wct */)
+
+ // Because the task surface was scaled down during the drag, we must use the animated
+ // bounds instead of the [startAbsBounds].
+ val tx: SurfaceControl.Transaction = transactionSupplier.get()
+ ValueAnimator.ofObject(rectEvaluator, animStartBounds, endBounds)
+ .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ .apply {
+ addUpdateListener { animator ->
+ val animBounds = animator.animatedValue as Rect
+ tx.apply {
+ setScale(draggedTaskLeash, 1f, 1f)
+ setPosition(
+ draggedTaskLeash,
+ animBounds.left.toFloat(),
+ animBounds.top.toFloat()
+ )
+ setWindowCrop(
+ draggedTaskLeash,
+ animBounds.width(),
+ animBounds.height()
+ )
+ }
+ state.windowDecoration.updateResizeVeil(tx, animBounds)
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ state.windowDecoration.hideResizeVeil()
+ startTransitionFinishCb.onTransitionFinished(null /* null */)
+ clearState()
+ }
+ })
+ start()
+ }
+ } else if (isCancelTransition) {
+ info.changes.forEach { change ->
+ t.show(change.leash)
+ startTransactionFinishT.show(change.leash)
+ }
+ t.apply()
+ finishCallback.onTransitionFinished(null /* wct */)
+ startTransitionFinishCb.onTransitionFinished(null /* wct */)
+ clearState()
+ }
+ }
+
+ override fun handleRequest(
+ transition: IBinder,
+ request: TransitionRequestInfo
+ ): WindowContainerTransaction? {
+ // Only handle transitions started from shell.
+ return null
+ }
+
+ private fun isHomeChange(change: Change): Boolean {
+ return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME
+ }
+
+ private fun startCancelAnimation() {
+ val state = requireTransitionState()
+ val dragToDesktopAnimator = state.dragAnimator
+
+ val draggedTaskChange = state.draggedTaskChange
+ ?: throw IllegalStateException("Expected non-null task change")
+ val sc = draggedTaskChange.leash
+ // TODO(b/301106941): Don't end the animation and start one to scale it back, merge them
+ // instead.
+ // End the animation that shrinks the window when task is first dragged from fullscreen
+ dragToDesktopAnimator.endAnimator()
+ // Then animate the scaled window back to its original bounds.
+ val x: Float = dragToDesktopAnimator.position.x
+ val y: Float = dragToDesktopAnimator.position.y
+ val targetX = draggedTaskChange.endAbsBounds.left
+ val targetY = draggedTaskChange.endAbsBounds.top
+ val dx = targetX - x
+ val dy = targetY - y
+ val tx: SurfaceControl.Transaction = transactionSupplier.get()
+ ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE, 1f)
+ .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
+ .apply {
+ addUpdateListener { animator ->
+ val scale = animator.animatedValue as Float
+ val fraction = animator.animatedFraction
+ val animX = x + (dx * fraction)
+ val animY = y + (dy * fraction)
+ tx.apply {
+ setPosition(sc, animX, animY)
+ setScale(sc, scale, scale)
+ show(sc)
+ apply()
+ }
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ dragToDesktopStateListener?.onCancelToDesktopAnimationEnd(tx)
+ // Start the cancel transition to restore order.
+ startCancelDragToDesktopTransition()
+ }
+ })
+ start()
+ }
+ }
+
+ private fun startCancelDragToDesktopTransition() {
+ val state = requireTransitionState()
+ val wct = WindowContainerTransaction()
+ when (state) {
+ is TransitionState.FromFullscreen -> {
+ val wc = state.draggedTaskChange?.container
+ ?: error("Dragged task should be non-null before cancelling")
+ wct.reorder(wc, true /* toTop */)
+ }
+ is TransitionState.FromSplit -> {
+ val wc = state.splitRootChange?.container
+ ?: error("Split root should be non-null before cancelling")
+ wct.reorder(wc, true /* toTop */)
+ }
+ }
+ val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling")
+ wct.restoreTransientOrder(homeWc)
+
+ state.cancelTransitionToken = transitions.startTransition(
+ TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
+ }
+
+ private fun clearState() {
+ transitionState = null
+ }
+
+ private fun isSplitTask(taskId: Int): Boolean {
+ return splitScreenController?.isTaskInSplitScreen(taskId) ?: false
+ }
+
+ private fun requireTransitionState(): TransitionState {
+ return transitionState ?: error("Expected non-null transition state")
+ }
+
+ interface DragToDesktopStateListener {
+ fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction)
+ fun onCancelToDesktopAnimationEnd(tx: SurfaceControl.Transaction)
+ }
+
+ sealed class TransitionState {
+ abstract val draggedTaskId: Int
+ abstract val dragAnimator: MoveToDesktopAnimator
+ abstract val windowDecoration: DesktopModeWindowDecoration
+ abstract val startTransitionToken: IBinder
+ abstract var startTransitionFinishCb: Transitions.TransitionFinishCallback?
+ abstract var startTransitionFinishTransaction: SurfaceControl.Transaction?
+ abstract var cancelTransitionToken: IBinder?
+ abstract var homeToken: WindowContainerToken?
+ abstract var draggedTaskChange: Change?
+ abstract var cancelled: Boolean
+
+ data class FromFullscreen(
+ override val draggedTaskId: Int,
+ override val dragAnimator: MoveToDesktopAnimator,
+ override val windowDecoration: DesktopModeWindowDecoration,
+ override val startTransitionToken: IBinder,
+ override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+ override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+ override var cancelTransitionToken: IBinder? = null,
+ override var homeToken: WindowContainerToken? = null,
+ override var draggedTaskChange: Change? = null,
+ override var cancelled: Boolean = false,
+ ) : TransitionState()
+ data class FromSplit(
+ override val draggedTaskId: Int,
+ override val dragAnimator: MoveToDesktopAnimator,
+ override val windowDecoration: DesktopModeWindowDecoration,
+ override val startTransitionToken: IBinder,
+ override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
+ override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
+ override var cancelTransitionToken: IBinder? = null,
+ override var homeToken: WindowContainerToken? = null,
+ override var draggedTaskChange: Change? = null,
+ override var cancelled: Boolean = false,
+ var splitRootChange: Change? = null,
+ ) : TransitionState()
+ }
+
+ companion object {
+ /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
+ private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 024465b..605600f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -18,12 +18,13 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_MOVE_TO_DESKTOP;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
@@ -38,11 +39,9 @@
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration;
-import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -60,8 +59,6 @@
public static final int FREEFORM_ANIMATION_DURATION = 336;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
- private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
- private MoveToDesktopAnimator mMoveToDesktopAnimator;
private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
public EnterDesktopTaskTransitionHandler(
@@ -77,61 +74,6 @@
}
/**
- * Starts Transition of a given type
- * @param type Transition type
- * @param wct WindowContainerTransaction for transition
- * @param onAnimationEndCallback to be called after animation
- */
- private void startTransition(@WindowManager.TransitionType int type,
- @NonNull WindowContainerTransaction wct,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- mOnAnimationFinishedCallback = onAnimationEndCallback;
- final IBinder token = mTransitions.startTransition(type, wct, this);
- mPendingTransitionTokens.add(token);
- }
-
- /**
- * Starts Transition of type TRANSIT_START_DRAG_TO_DESKTOP_MODE
- * @param wct WindowContainerTransaction for transition
- * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
- * to desktop animation
- * @param onAnimationEndCallback to be called after animation
- */
- public void startMoveToDesktop(@NonNull WindowContainerTransaction wct,
- @NonNull MoveToDesktopAnimator moveToDesktopAnimator,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- mMoveToDesktopAnimator = moveToDesktopAnimator;
- startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
- onAnimationEndCallback);
- }
-
- /**
- * Starts Transition of type TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
- * @param wct WindowContainerTransaction for transition
- * @param onAnimationEndCallback to be called after animation
- */
- public void finalizeMoveToDesktop(@NonNull WindowContainerTransaction wct,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- startTransition(Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, wct,
- onAnimationEndCallback);
- }
-
- /**
- * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
- * @param wct WindowContainerTransaction for transition
- * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
- * to desktop animation
- * @param onAnimationEndCallback to be called after animation
- */
- public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct,
- MoveToDesktopAnimator moveToDesktopAnimator,
- Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
- mMoveToDesktopAnimator = moveToDesktopAnimator;
- startTransition(Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE, wct,
- onAnimationEndCallback);
- }
-
- /**
* Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
* @param wct WindowContainerTransaction for transition
* @param decor {@link DesktopModeWindowDecoration} of task being animated
@@ -139,8 +81,8 @@
public void moveToDesktop(@NonNull WindowContainerTransaction wct,
DesktopModeWindowDecoration decor) {
mDesktopModeWindowDecoration = decor;
- startTransition(Transitions.TRANSIT_MOVE_TO_DESKTOP, wct,
- null /* onAnimationEndCallback */);
+ final IBinder token = mTransitions.startTransition(TRANSIT_MOVE_TO_DESKTOP, wct, this);
+ mPendingTransitionTokens.add(token);
}
@Override
@@ -182,30 +124,11 @@
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (type == Transitions.TRANSIT_MOVE_TO_DESKTOP
+ if (type == TRANSIT_MOVE_TO_DESKTOP
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return animateMoveToDesktop(change, startT, finishCallback);
}
- if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- return animateStartDragToDesktopMode(change, startT, finishT, finishCallback);
- }
-
- final Rect endBounds = change.getEndAbsBounds();
- if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && !endBounds.isEmpty()) {
- return animateFinalizeDragToDesktopMode(change, startT, finishT, finishCallback,
- endBounds);
- }
-
- if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- return animateCancelDragToDesktopMode(change, startT, finishT, finishCallback,
- endBounds);
- }
-
return false;
}
@@ -248,142 +171,6 @@
return true;
}
- private boolean animateStartDragToDesktopMode(
- @NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startT,
- @NonNull SurfaceControl.Transaction finishT,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- // Transitioning to freeform but keeping fullscreen bounds, so the crop is set
- // to null and we don't require an animation
- final SurfaceControl sc = change.getLeash();
- startT.setWindowCrop(sc, null);
-
- if (mMoveToDesktopAnimator == null
- || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
- Slog.e(TAG, "No animator available for this transition");
- return false;
- }
-
- // Calculate and set position of the task
- final PointF position = mMoveToDesktopAnimator.getPosition();
- startT.setPosition(sc, position.x, position.y);
- finishT.setPosition(sc, position.x, position.y);
-
- startT.apply();
-
- mTransitions.getMainExecutor().execute(() -> finishCallback.onTransitionFinished(null));
-
- return true;
- }
-
- private boolean animateFinalizeDragToDesktopMode(
- @NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startT,
- @NonNull SurfaceControl.Transaction finishT,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull Rect endBounds) {
- // This Transition animates a task to freeform bounds after being dragged into freeform
- // mode and brings the remaining freeform tasks to front
- final SurfaceControl sc = change.getLeash();
- startT.setWindowCrop(sc, endBounds.width(),
- endBounds.height());
- startT.apply();
-
- // End the animation that shrinks the window when task is first dragged from fullscreen
- if (mMoveToDesktopAnimator != null) {
- mMoveToDesktopAnimator.endAnimator();
- }
-
- // We want to find the scale of the current bounds relative to the end bounds. The
- // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be
- // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to
- // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds
- final ValueAnimator animator =
- ValueAnimator.ofFloat(
- MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
- animator.setDuration(FREEFORM_ANIMATION_DURATION);
- final SurfaceControl.Transaction t = mTransactionSupplier.get();
- animator.addUpdateListener(animation -> {
- final float animationValue = (float) animation.getAnimatedValue();
- t.setScale(sc, animationValue, animationValue);
-
- final float animationWidth = endBounds.width() * animationValue;
- final float animationHeight = endBounds.height() * animationValue;
- final int animationX = endBounds.centerX() - (int) (animationWidth / 2);
- final int animationY = endBounds.centerY() - (int) (animationHeight / 2);
-
- t.setPosition(sc, animationX, animationY);
- t.apply();
- });
-
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mOnAnimationFinishedCallback != null) {
- mOnAnimationFinishedCallback.accept(finishT);
- }
- mTransitions.getMainExecutor().execute(
- () -> finishCallback.onTransitionFinished(null));
- }
- });
-
- animator.start();
- return true;
- }
- private boolean animateCancelDragToDesktopMode(
- @NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startT,
- @NonNull SurfaceControl.Transaction finishT,
- @NonNull Transitions.TransitionFinishCallback finishCallback,
- @NonNull Rect endBounds) {
- // This Transition animates a task to fullscreen after being dragged from the status
- // bar and then released back into the status bar area
- final SurfaceControl sc = change.getLeash();
- // Hide the first (fullscreen) frame because the animation will start from the smaller
- // scale size.
- startT.hide(sc)
- .setWindowCrop(sc, endBounds.width(), endBounds.height())
- .apply();
-
- if (mMoveToDesktopAnimator == null
- || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
- Slog.e(TAG, "No animator available for this transition");
- return false;
- }
-
- // End the animation that shrinks the window when task is first dragged from fullscreen
- mMoveToDesktopAnimator.endAnimator();
-
- final ValueAnimator animator = new ValueAnimator();
- animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f);
- animator.setDuration(FREEFORM_ANIMATION_DURATION);
- final SurfaceControl.Transaction t = mTransactionSupplier.get();
-
- // Get position of the task
- final float x = mMoveToDesktopAnimator.getPosition().x;
- final float y = mMoveToDesktopAnimator.getPosition().y;
-
- animator.addUpdateListener(animation -> {
- final float scale = (float) animation.getAnimatedValue();
- t.setPosition(sc, x * (1 - scale), y * (1 - scale))
- .setScale(sc, scale, scale)
- .show(sc)
- .apply();
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mOnAnimationFinishedCallback != null) {
- mOnAnimationFinishedCallback.accept(finishT);
- }
- mTransitions.getMainExecutor().execute(
- () -> finishCallback.onTransitionFinished(null));
- }
- });
- animator.start();
- return true;
- }
-
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index fc34772..63cef9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -107,7 +107,7 @@
private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
- private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+ private static final float MENU_BACKGROUND_ALPHA = 0.54f;
private static final float DISABLED_ACTION_ALPHA = 0.54f;
private int mMenuState;
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 723a4a7..193a4fb 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
@@ -60,6 +60,7 @@
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import android.animation.Animator;
@@ -424,7 +425,8 @@
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
- Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition);
+ final int type = getTransitionTypeFromInfo(info);
+ Animation a = loadAnimation(type, info, change, wallpaperTransit, isDreamTransition);
if (a != null) {
if (isTask) {
final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
@@ -660,12 +662,11 @@
}
@Nullable
- private Animation loadAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, int wallpaperTransit,
- boolean isDreamTransition) {
+ private Animation loadAnimation(@WindowManager.TransitionType int type,
+ @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
+ int wallpaperTransit, boolean isDreamTransition) {
Animation a;
- final int type = info.getType();
final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
@@ -721,7 +722,7 @@
return null;
} else {
a = loadAttributeAnimation(
- info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
+ type, info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
}
if (a != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index d07d2b7b6..b012d35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -45,6 +46,7 @@
import android.graphics.Shader;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.window.ScreenCapture;
@@ -61,10 +63,10 @@
/** Loads the animation that is defined through attribute id for the given transition. */
@Nullable
- public static Animation loadAttributeAnimation(@NonNull TransitionInfo info,
+ public static Animation loadAttributeAnimation(@WindowManager.TransitionType int type,
+ @NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, int wallpaperTransit,
@NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) {
- final int type = info.getType();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
final boolean enter = TransitionUtil.isOpeningType(changeMode);
@@ -186,6 +188,38 @@
return options.getCustomActivityTransition(isOpen);
}
+ /**
+ * Gets the final transition type from {@link TransitionInfo} for determining the animation.
+ */
+ public static int getTransitionTypeFromInfo(@NonNull TransitionInfo info) {
+ final int type = info.getType();
+ // If the info transition type is opening transition, iterate its changes to see if it
+ // has any opening change, if none, returns TRANSIT_CLOSE type for closing animation.
+ if (type == TRANSIT_OPEN) {
+ boolean hasOpenTransit = false;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if ((change.getTaskInfo() != null || change.hasFlags(FLAG_IS_DISPLAY))
+ && !TransitionUtil.isOrderOnly(change)) {
+ // This isn't an activity-level transition.
+ return type;
+ }
+ if (change.getTaskInfo() != null
+ && change.hasFlags(FLAG_IS_DISPLAY | FLAGS_IS_NON_APP_WINDOW)) {
+ // Ignore non-activity containers.
+ continue;
+ }
+ if (change.getMode() == TRANSIT_OPEN) {
+ hasOpenTransit = true;
+ break;
+ }
+ }
+ if (!hasOpenTransit) {
+ return TRANSIT_CLOSE;
+ }
+ }
+ return type;
+ }
+
static Animation loadCustomActivityTransition(
@NonNull TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim,
TransitionInfo.AnimationOptions options, boolean enter,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index ab5c063..41ec33c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -150,19 +150,19 @@
/** Transition type for maximize to freeform transition. */
public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
- /** Transition type for starting the move to desktop mode. */
- public static final int TRANSIT_START_DRAG_TO_DESKTOP_MODE =
+ /** Transition type for starting the drag to desktop mode. */
+ public static final int TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP =
WindowManager.TRANSIT_FIRST_CUSTOM + 10;
- /** Transition type for finalizing the move to desktop mode. */
- public static final int TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE =
+ /** Transition type for finalizing the drag to desktop mode. */
+ public static final int TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP =
WindowManager.TRANSIT_FIRST_CUSTOM + 11;
/** Transition type to fullscreen from desktop mode. */
public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
- /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */
- public static final int TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE =
+ /** Transition type to cancel the drag to desktop mode. */
+ public static final int TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP =
WindowManager.TRANSIT_FIRST_CUSTOM + 13;
/** Transition type to animate the toggle resize between the max and default desktop sizes. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index e206039..3add6f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -268,13 +268,19 @@
@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change) {
if (change.getMode() == WindowManager.TRANSIT_CHANGE
- && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
- || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
- || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+ && (info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
|| info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
|| info.getType() == Transitions.TRANSIT_MOVE_TO_DESKTOP)) {
mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
.addTransitionPausingRelayout(transition);
+ } else if (change.getMode() == WindowManager.TRANSIT_TO_BACK
+ && info.getType() == Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+ && change.getTaskInfo() != null) {
+ final DesktopModeWindowDecoration decor =
+ mWindowDecorByTaskId.get(change.getTaskInfo().taskId);
+ if (decor != null) {
+ decor.addTransitionPausingRelayout(transition);
+ }
}
}
@@ -765,10 +771,8 @@
mMoveToDesktopAnimator = null;
return;
} else if (mMoveToDesktopAnimator != null) {
- relevantDecor.incrementRelayoutBlock();
mDesktopTasksController.ifPresent(
- c -> c.cancelMoveToDesktop(relevantDecor.mTaskInfo,
- mMoveToDesktopAnimator));
+ c -> c.cancelDragToDesktop(relevantDecor.mTaskInfo));
mMoveToDesktopAnimator = null;
return;
}
@@ -790,15 +794,24 @@
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
- closeOtherSplitTask(relevantDecor.mTaskInfo.taskId);
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
- mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface);
+ mContext, mDragToDesktopAnimationStartBounds,
+ relevantDecor.mTaskInfo, relevantDecor.mTaskSurface);
mDesktopTasksController.ifPresent(
- c -> c.startMoveToDesktop(relevantDecor.mTaskInfo,
- mDragToDesktopAnimationStartBounds,
- mMoveToDesktopAnimator));
- mMoveToDesktopAnimator.startAnimation();
+ c -> {
+ final int taskId = relevantDecor.mTaskInfo.taskId;
+ relevantDecor.incrementRelayoutBlock();
+ if (isTaskInSplitScreen(taskId)) {
+ final DesktopModeWindowDecoration otherDecor =
+ mWindowDecorByTaskId.get(
+ getOtherSplitTask(taskId).taskId);
+ if (otherDecor != null) {
+ otherDecor.incrementRelayoutBlock();
+ }
+ }
+ c.startDragToDesktop(relevantDecor.mTaskInfo,
+ mMoveToDesktopAnimator, relevantDecor);
+ });
}
}
if (mMoveToDesktopAnimator != null) {
@@ -837,7 +850,6 @@
*/
private void animateToDesktop(DesktopModeWindowDecoration relevantDecor,
MotionEvent ev) {
- relevantDecor.incrementRelayoutBlock();
centerAndMoveToDesktopWithAnimation(relevantDecor, ev);
}
@@ -853,15 +865,15 @@
final SurfaceControl sc = relevantDecor.mTaskSurface;
final Rect endBounds = calculateFreeformBounds(ev.getDisplayId(), DRAG_FREEFORM_SCALE);
final Transaction t = mTransactionFactory.get();
- final float diffX = endBounds.centerX() - ev.getX();
- final float diffY = endBounds.top - ev.getY();
- final float startingX = ev.getX() - DRAG_FREEFORM_SCALE
+ final float diffX = endBounds.centerX() - ev.getRawX();
+ final float diffY = endBounds.top - ev.getRawY();
+ final float startingX = ev.getRawX() - DRAG_FREEFORM_SCALE
* mDragToDesktopAnimationStartBounds.width() / 2;
animator.addUpdateListener(animation -> {
final float animatorValue = (float) animation.getAnimatedValue();
final float x = startingX + diffX * animatorValue;
- final float y = ev.getY() + diffY * animatorValue;
+ final float y = ev.getRawY() + diffY * animatorValue;
t.setPosition(sc, x, y);
t.apply();
});
@@ -869,9 +881,11 @@
@Override
public void onAnimationEnd(Animator animation) {
mDesktopTasksController.ifPresent(
- c -> c.onDragPositioningEndThroughStatusBar(
- relevantDecor.mTaskInfo,
- calculateFreeformBounds(ev.getDisplayId(), FINAL_FREEFORM_SCALE)));
+ c -> {
+ c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo,
+ calculateFreeformBounds(ev.getDisplayId(),
+ FINAL_FREEFORM_SCALE));
+ });
}
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
index b2267dd..af05523 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MoveToDesktopAnimator.kt
@@ -2,10 +2,12 @@
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
import android.graphics.PointF
import android.graphics.Rect
import android.view.MotionEvent
import android.view.SurfaceControl
+import com.android.internal.policy.ScreenDecorationsUtils
/**
* Creates an animator to shrink and position task after a user drags a fullscreen task from
@@ -14,6 +16,7 @@
* accessed by the EnterDesktopTaskTransitionHandler.
*/
class MoveToDesktopAnimator @JvmOverloads constructor(
+ private val context: Context,
private val startBounds: Rect,
private val taskInfo: RunningTaskInfo,
private val taskSurface: SurfaceControl,
@@ -33,9 +36,11 @@
.setDuration(ANIMATION_DURATION.toLong())
.apply {
val t = SurfaceControl.Transaction()
+ val cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
addUpdateListener { animation ->
val animatorValue = animation.animatedValue as Float
t.setScale(taskSurface, animatorValue, animatorValue)
+ .setCornerRadius(taskSurface, cornerRadius)
.apply()
}
}
@@ -44,19 +49,40 @@
val position: PointF = PointF(0.0f, 0.0f)
/**
+ * Whether motion events from the drag gesture should affect the dragged surface or not. Used
+ * to disallow moving the surface's position prematurely since it should not start moving at
+ * all until the drag-to-desktop transition is ready to animate and the wallpaper/home are
+ * ready to be revealed behind the dragged/scaled task.
+ */
+ private var allowSurfaceChangesOnMove = false
+
+ /**
* Starts the animation that scales the task down.
*/
fun startAnimation() {
+ allowSurfaceChangesOnMove = true
dragToDesktopAnimator.start()
}
/**
- * Uses the position of the motion event and the current scale of the task as defined by the
- * ValueAnimator to update the local position variable and set the task surface's position
+ * Uses the position of the motion event of the drag-to-desktop gesture to update the dragged
+ * task's position on screen to follow the touch point. Note that the position change won't
+ * be applied immediately always, such as near the beginning where it waits until the wallpaper
+ * or home are visible behind it. Once they're visible the surface will catch-up to the most
+ * recent touch position.
*/
fun updatePosition(ev: MotionEvent) {
- position.x = ev.x - animatedTaskWidth / 2
- position.y = ev.y
+ // Using rawX/Y because when dragging a task in split, the local X/Y is relative to the
+ // split stages, but the split task surface is re-parented to the task display area to
+ // allow dragging beyond its stage across any region of the display. Because of that, the
+ // rawX/Y are more true to where the gesture is on screen and where the surface should be
+ // positioned.
+ position.x = ev.rawX - animatedTaskWidth / 2
+ position.y = ev.rawY
+
+ if (!allowSurfaceChangesOnMove) {
+ return
+ }
val t = transactionFactory()
t.setPosition(taskSurface, position.x, position.y)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 4d11dfb..386983c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -124,6 +124,10 @@
":WMShellFlickerTestsPipCommon-src",
],
static_libs: ["WMShellFlickerTestsBase"],
+ test_suites: [
+ "device-tests",
+ "csuite",
+ ],
}
csuite_test {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 6df6539..89ecc29 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -71,9 +71,9 @@
<!-- Enable mocking GPS location by the test app -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+ value="appops set com.android.shell android:mock_location allow"/>
<option name="teardown-command"
- value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+ value="appops set com.android.shell android:mock_location deny"/>
</target_preparer>
<!-- Needed for pushing the trace config file -->
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index bd8b005..182a908 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -31,10 +31,18 @@
abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
protected abstract val standardAppHelper: StandardAppHelper
+ protected abstract val permissions: Array<String>
+
@FlickerBuilderProvider
override fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withoutScreenRecorder()
+ instrumentation.uiAutomation.adoptShellPermissionIdentity()
+ for (permission in permissions) {
+ instrumentation.uiAutomation.grantRuntimePermission(
+ standardAppHelper.packageName,
+ permission
+ )
+ }
setup { flicker.scenario.setIsTablet(tapl.isTablet) }
transition()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index 4da52ef..d06cf77 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.apps
+import android.Manifest
import android.content.Context
import android.location.Criteria
import android.location.Location
@@ -64,6 +65,9 @@
open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation)
+ override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS,
+ Manifest.permission.ACCESS_FINE_LOCATION)
+
val locationManager: LocationManager =
instrumentation.context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val mainHandler = Handler(Looper.getMainLooper())
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 5498e8c..32f1259 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.apps
+import android.Manifest
import android.platform.test.annotations.Postsubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -62,6 +63,8 @@
open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: NetflixAppHelper = NetflixAppHelper(instrumentation)
+ override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
+
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
standardAppHelper.launchViaIntent(
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index d8afc25..509b32c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.apps
+import android.Manifest
import android.platform.test.annotations.Postsubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.YouTubeAppHelper
@@ -58,6 +59,8 @@
open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
+ override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
+
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
standardAppHelper.launchViaIntent(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index ebcb640..fde6acb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -100,6 +100,7 @@
@Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
@Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
ToggleResizeDesktopTaskTransitionHandler
+ @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
@Mock lateinit var launchAdjacentController: LaunchAdjacentController
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
@Mock lateinit var splitScreenController: SplitScreenController
@@ -127,7 +128,7 @@
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
controller = createController()
- controller.splitScreenController = splitScreenController
+ controller.setSplitScreenController(splitScreenController)
shellInit.init()
@@ -150,6 +151,7 @@
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
mToggleResizeDesktopTaskTransitionHandler,
+ dragToDesktopTransitionHandler,
desktopModeTaskRepository,
launchAdjacentController,
recentsTransitionHandler,
@@ -757,6 +759,7 @@
private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createSplitScreenTask(displayId)
+ whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
new file mode 100644
index 0000000..a5629c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt
@@ -0,0 +1,211 @@
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WindowingMode
+import android.graphics.PointF
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.TransitionInfo.FLAG_IS_WALLPAPER
+import androidx.test.filters.SmallTest
+import com.android.server.testutils.any
+import com.android.server.testutils.mock
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.splitscreen.SplitScreenController
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
+import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
+import java.util.function.Supplier
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/** Tests of [DragToDesktopTransitionHandler]. */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DragToDesktopTransitionHandlerTest : ShellTestCase() {
+
+ @Mock private lateinit var transitions: Transitions
+ @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var splitScreenController: SplitScreenController
+
+ private val transactionSupplier = Supplier { mock<SurfaceControl.Transaction>() }
+
+ private lateinit var handler: DragToDesktopTransitionHandler
+
+ @Before
+ fun setUp() {
+ handler =
+ DragToDesktopTransitionHandler(
+ context,
+ transitions,
+ taskDisplayAreaOrganizer,
+ transactionSupplier
+ )
+ .apply { setSplitScreenController(splitScreenController) }
+ }
+
+ @Test
+ fun startDragToDesktop_animateDragWhenReady() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+
+ // Now it's ready to animate.
+ handler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+ draggedTask = task
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ verify(dragAnimator).startAnimation()
+ }
+
+ @Test
+ fun startDragToDesktop_cancelledBeforeReady_startCancelTransition() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started and is ready to animate.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+
+ handler.cancelDragToDesktopTransition()
+
+ handler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+ draggedTask = task
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ // Don't even animate the "drag" since it was already cancelled.
+ verify(dragAnimator, never()).startAnimation()
+ // Instead, start the cancel transition.
+ verify(transitions)
+ .startTransition(eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), any(), eq(handler))
+ }
+
+ @Test
+ fun cancelDragToDesktop_startWasReady_cancel() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ whenever(dragAnimator.position).thenReturn(PointF())
+ // Simulate transition is started and is ready to animate.
+ val transition = startDragToDesktopTransition(task, dragAnimator)
+ handler.startAnimation(
+ transition = transition,
+ info =
+ createTransitionInfo(
+ type = TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP,
+ draggedTask = task
+ ),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ finishCallback = {}
+ )
+
+ // Then user cancelled after it had already started.
+ handler.cancelDragToDesktopTransition()
+
+ // Cancel animation should run since it had already started.
+ verify(dragAnimator).endAnimator()
+ }
+
+ @Test
+ fun cancelDragToDesktop_startWasNotReady_animateCancel() {
+ val task = createTask()
+ val dragAnimator = mock<MoveToDesktopAnimator>()
+ // Simulate transition is started and is ready to animate.
+ startDragToDesktopTransition(task, dragAnimator)
+
+ // Then user cancelled before the transition was ready and animated.
+ handler.cancelDragToDesktopTransition()
+
+ // No need to animate the cancel since the start animation couldn't even start.
+ verifyZeroInteractions(dragAnimator)
+ }
+
+ private fun startDragToDesktopTransition(
+ task: RunningTaskInfo,
+ dragAnimator: MoveToDesktopAnimator
+ ): IBinder {
+ val token = mock<IBinder>()
+ whenever(
+ transitions.startTransition(
+ eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP),
+ any(),
+ eq(handler)
+ )
+ )
+ .thenReturn(token)
+ handler.startDragToDesktopTransition(task.taskId, dragAnimator, mock())
+ return token
+ }
+
+ private fun createTask(
+ @WindowingMode windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+ isHome: Boolean = false,
+ ): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setActivityType(if (isHome) ACTIVITY_TYPE_HOME else ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(windowingMode)
+ .build()
+ .also {
+ whenever(splitScreenController.isTaskInSplitScreen(it.taskId))
+ .thenReturn(windowingMode == WINDOWING_MODE_MULTI_WINDOW)
+ }
+ }
+
+ private fun createTransitionInfo(type: Int, draggedTask: RunningTaskInfo): TransitionInfo {
+ return TransitionInfo(type, 0 /* flags */).apply {
+ addChange( // Home.
+ TransitionInfo.Change(mock(), mock()).apply {
+ parent = null
+ taskInfo =
+ TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+ flags = flags or FLAG_IS_WALLPAPER
+ }
+ )
+ addChange( // Dragged Task.
+ TransitionInfo.Change(mock(), mock()).apply {
+ parent = null
+ taskInfo = draggedTask
+ }
+ )
+ addChange( // Wallpaper.
+ TransitionInfo.Change(mock(), mock()).apply {
+ parent = null
+ taskInfo = null
+ flags = flags or FLAG_IS_WALLPAPER
+ }
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
deleted file mode 100644
index 772d97d..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.desktopmode;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.annotation.NonNull;
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.IWindowContainerToken;
-import android.window.TransitionInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;
-
-import junit.framework.AssertionFailedError;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.Supplier;
-
-/** Tests of {@link com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler} */
-@SmallTest
-public class EnterDesktopTaskTransitionHandlerTest {
-
- @Mock
- private Transitions mTransitions;
- @Mock
- IBinder mToken;
- @Mock
- Supplier<SurfaceControl.Transaction> mTransactionFactory;
- @Mock
- SurfaceControl.Transaction mStartT;
- @Mock
- SurfaceControl.Transaction mFinishT;
- @Mock
- SurfaceControl.Transaction mAnimationT;
- @Mock
- Transitions.TransitionFinishCallback mTransitionFinishCallback;
- @Mock
- ShellExecutor mExecutor;
- @Mock
- SurfaceControl mSurfaceControl;
- @Mock
- MoveToDesktopAnimator mMoveToDesktopAnimator;
- @Mock
- PointF mPosition;
-
- private EnterDesktopTaskTransitionHandler mEnterDesktopTaskTransitionHandler;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- doReturn(mExecutor).when(mTransitions).getMainExecutor();
- doReturn(mAnimationT).when(mTransactionFactory).get();
- doReturn(mPosition).when(mMoveToDesktopAnimator).getPosition();
-
- mEnterDesktopTaskTransitionHandler = new EnterDesktopTaskTransitionHandler(mTransitions,
- mTransactionFactory);
- }
-
- @Test
- public void testEnterFreeformAnimation() {
- final int taskId = 1;
- WindowContainerTransaction wct = new WindowContainerTransaction();
- doReturn(mToken).when(mTransitions)
- .startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
- mEnterDesktopTaskTransitionHandler);
- doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId();
-
- mEnterDesktopTaskTransitionHandler.startMoveToDesktop(wct,
- mMoveToDesktopAnimator, null);
-
- TransitionInfo.Change change =
- createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
- TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE,
- change);
-
-
- assertTrue(mEnterDesktopTaskTransitionHandler
- .startAnimation(mToken, info, mStartT, mFinishT, mTransitionFinishCallback));
-
- verify(mStartT).setWindowCrop(mSurfaceControl, null);
- verify(mStartT).apply();
- }
-
- @Test
- public void testTransitEnterDesktopModeAnimation() throws Throwable {
- final int transitionType = Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE;
- final int taskId = 1;
- WindowContainerTransaction wct = new WindowContainerTransaction();
- doReturn(mToken).when(mTransitions)
- .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler);
- mEnterDesktopTaskTransitionHandler.finalizeMoveToDesktop(wct, null);
-
- TransitionInfo.Change change =
- createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
- change.setEndAbsBounds(new Rect(0, 0, 1, 1));
- TransitionInfo info = createTransitionInfo(
- Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, change);
-
- runOnUiThread(() -> {
- try {
- assertTrue(mEnterDesktopTaskTransitionHandler
- .startAnimation(mToken, info, mStartT, mFinishT,
- mTransitionFinishCallback));
- } catch (Exception e) {
- throw new AssertionFailedError(e.getMessage());
- }
- });
-
- verify(mStartT).setWindowCrop(mSurfaceControl, change.getEndAbsBounds().width(),
- change.getEndAbsBounds().height());
- verify(mStartT).apply();
- }
-
- private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
- @WindowConfiguration.WindowingMode int windowingMode) {
- final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
- taskInfo.taskId = taskId;
- taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
- final TransitionInfo.Change change = new TransitionInfo.Change(
- new WindowContainerToken(mock(IWindowContainerToken.class)), mSurfaceControl);
- change.setMode(type);
- change.setTaskInfo(taskInfo);
- return change;
- }
-
- private static TransitionInfo createTransitionInfo(
- @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) {
- TransitionInfo info = new TransitionInfo(type, 0);
- info.addChange(change);
- return info;
- }
-
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 4e300d9..01c9bd0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -40,6 +40,8 @@
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -93,6 +95,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.R;
+import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -1463,6 +1467,43 @@
assertEquals(0, mDefaultHandler.activeCount());
}
+ @Test
+ public void testCloseTransitAnimationWhenClosingChangesExists() {
+ Transitions transitions = createTestTransitions();
+ Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
+ transitions.registerObserver(observer);
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+ final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false,
+ Transitions.TAG);
+ spyOn(transitionAnimation);
+
+ // Creating a transition by the app hooking the back key event to start the
+ // previous activity with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ // flags in order to clear the top activity and bring the exist previous activity to front.
+ // Expects the activity transition should playing the close animation instead the initiated
+ // open animation made by startActivity.
+ IBinder transitToken = new Binder();
+ transitions.requestStartTransition(transitToken,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_CLOSE).addChange(TRANSIT_TO_FRONT).build();
+ transitions.onTransitionReady(transitToken, info, new StubTransaction(),
+ new StubTransaction());
+
+ final int type = getTransitionTypeFromInfo(info);
+ assertEquals(TRANSIT_CLOSE, type);
+
+ TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(0), 0,
+ transitionAnimation, false);
+ verify(transitionAnimation).loadDefaultAnimationAttr(
+ eq(R.styleable.WindowAnimation_activityCloseExitAnimation), anyBoolean());
+
+ TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(1), 0,
+ transitionAnimation, false);
+ verify(transitionAnimation).loadDefaultAnimationAttr(
+ eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean());
+ }
+
class ChangeBuilder {
final TransitionInfo.Change mChange;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 31fc929..008ea3a 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -589,10 +589,40 @@
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
+bool SkiaCanvas::useGainmapShader(Bitmap& bitmap) {
+ // If the bitmap doesn't have a gainmap, don't use the gainmap shader
+ if (!bitmap.hasGainmap()) return false;
+
+ // If we don't have an owned canvas, then we're either hardware accelerated or drawing
+ // to a picture - use the gainmap shader out of caution. Ideally a picture canvas would
+ // use a drawable here instead to defer making that decision until the last possible
+ // moment
+ if (!mCanvasOwned) return true;
+
+ auto info = mCanvasOwned->imageInfo();
+
+ // If it's an unknown colortype then it's not a bitmap-backed canvas
+ if (info.colorType() == SkColorType::kUnknown_SkColorType) return true;
+
+ skcms_TransferFunction tfn;
+ info.colorSpace()->transferFn(&tfn);
+
+ auto transferType = skcms_TransferFunction_getType(&tfn);
+ switch (transferType) {
+ case skcms_TFType_HLGish:
+ case skcms_TFType_HLGinvish:
+ case skcms_TFType_PQish:
+ return true;
+ case skcms_TFType_Invalid:
+ case skcms_TFType_sRGBish:
+ return false;
+ }
+}
+
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- if (bitmap.hasGainmap()) {
+ if (useGainmapShader(bitmap)) {
Paint gainmapPaint = paint ? *paint : Paint();
sk_sp<SkShader> gainmapShader = uirenderer::MakeGainmapShader(
image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
@@ -619,7 +649,7 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- if (bitmap.hasGainmap()) {
+ if (useGainmapShader(bitmap)) {
Paint gainmapPaint = paint ? *paint : Paint();
sk_sp<SkShader> gainmapShader = uirenderer::MakeGainmapShader(
image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index ced0224..4bf1790 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -223,6 +223,8 @@
void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode);
+ bool useGainmapShader(Bitmap& bitmap);
+
class Clip;
std::unique_ptr<SkCanvas> mCanvasOwned; // Might own a canvas we allocated.
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 7faa13c..447d3bb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1176,6 +1176,7 @@
case AudioFormat.ENCODING_PCM_FLOAT:
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
+ case AudioFormat.ENCODING_E_AC3_JOC:
mAudioFormat = audioFormat;
break;
default:
@@ -1188,20 +1189,12 @@
// Convenience method for the contructor's audio buffer size check.
- // preconditions:
- // mChannelCount is valid
- // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
- // or AudioFormat.ENCODING_PCM_FLOAT
// postcondition:
// mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
- // NB: this section is only valid with PCM data.
- // To update when supporting compressed formats
- int frameSizeInBytes = mChannelCount
- * (AudioFormat.getBytesPerSample(mAudioFormat));
- if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
+ if ((audioBufferSize % getFormat().getFrameSizeInBytes() != 0) || (audioBufferSize < 1)) {
throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
- + " (frame size " + frameSizeInBytes + ")");
+ + " (frame size " + getFormat().getFrameSizeInBytes() + ")");
}
mNativeBufferSizeInBytes = audioBufferSize;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index feb914f..757e9f8 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -630,7 +630,6 @@
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
ScopedLocalRef<jobject> audioDescriptor(env);
- gAudioPresentationFields.init(env);
ScopedLocalRef presentationsJObj(env, JAudioPresentationInfo::asJobject(
env, gAudioPresentationFields));
switch (mediaEvent.extraMetaData.getTag()) {
@@ -3731,6 +3730,7 @@
gFields.linearBlockInitID = env->GetMethodID(linearBlockClazz, "<init>", "()V");
gFields.linearBlockSetInternalStateID =
env->GetMethodID(linearBlockClazz, "setInternalStateLocked", "(JZ)V");
+ gAudioPresentationFields.init(env);
}
static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING
index fd394fc..7c71098 100644
--- a/native/android/TEST_MAPPING
+++ b/native/android/TEST_MAPPING
@@ -22,5 +22,15 @@
],
"file_patterns": ["performance_hint.cpp"]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsThermalTestCases",
+ "file_patterns": ["thermal.cpp"]
+ },
+ {
+ "name": "NativeThermalUnitTestCases",
+ "file_patterns": ["thermal.cpp"]
+ }
]
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f4be33c7..9f2a9ac 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -327,6 +327,7 @@
AThermal_registerThermalStatusListener; # introduced=30
AThermal_unregisterThermalStatusListener; # introduced=30
AThermal_getThermalHeadroom; # introduced=31
+ AThermal_getThermalHeadroomThresholds; # introduced=VanillaIceCream
APerformanceHint_getManager; # introduced=Tiramisu
APerformanceHint_createSession; # introduced=Tiramisu
APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu
@@ -348,6 +349,7 @@
LIBANDROID_PLATFORM {
global:
+ AThermal_setIThermalServiceForTesting;
APerformanceHint_setIHintManagerForTesting;
APerformanceHint_sendHint;
APerformanceHint_getThreadIds;
diff --git a/native/android/tests/thermal/Android.bp b/native/android/tests/thermal/Android.bp
new file mode 100644
index 0000000..8540d83
--- /dev/null
+++ b/native/android/tests/thermal/Android.bp
@@ -0,0 +1,65 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_test {
+ name: "NativeThermalUnitTestCases",
+
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["NativeThermalUnitTest.cpp"],
+
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libbinder",
+ "libpowermanager",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libbase",
+ "libgmock",
+ "libgtest",
+ ],
+ stl: "c++_shared",
+
+ test_suites: [
+ "device-tests",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ header_libs: [
+ "libandroid_headers_private",
+ ],
+}
diff --git a/native/android/tests/thermal/NativeThermalUnitTest.cpp b/native/android/tests/thermal/NativeThermalUnitTest.cpp
new file mode 100644
index 0000000..6d6861a
--- /dev/null
+++ b/native/android/tests/thermal/NativeThermalUnitTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#define LOG_TAG "NativeThermalUnitTest"
+
+#include <android/os/IThermalService.h>
+#include <android/thermal.h>
+#include <binder/IBinder.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thermal_private.h>
+
+using android::binder::Status;
+
+using namespace testing;
+using namespace android;
+using namespace android::os;
+
+class MockIThermalService : public IThermalService {
+public:
+ MOCK_METHOD(Status, registerThermalEventListener,
+ (const ::android::sp<::android::os::IThermalEventListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, registerThermalEventListenerWithType,
+ (const ::android::sp<::android::os::IThermalEventListener>& listener, int32_t type,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, unregisterThermalEventListener,
+ (const ::android::sp<::android::os::IThermalEventListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getCurrentTemperatures,
+ (::std::vector<::android::os::Temperature> * _aidl_return), (override));
+ MOCK_METHOD(Status, getCurrentTemperaturesWithType,
+ (int32_t type, ::std::vector<::android::os::Temperature>* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, registerThermalStatusListener,
+ (const ::android::sp<::android::os::IThermalStatusListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, unregisterThermalStatusListener,
+ (const ::android::sp<::android::os::IThermalStatusListener>& listener,
+ bool* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getCurrentThermalStatus, (int32_t * _aidl_return), (override));
+ MOCK_METHOD(Status, getCurrentCoolingDevices,
+ (::std::vector<::android::os::CoolingDevice> * _aidl_return), (override));
+ MOCK_METHOD(Status, getCurrentCoolingDevicesWithType,
+ (int32_t type, ::std::vector<::android::os::CoolingDevice>* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getThermalHeadroom, (int32_t forecastSeconds, float* _aidl_return),
+ (override));
+ MOCK_METHOD(Status, getThermalHeadroomThresholds, (::std::vector<float> * _aidl_return),
+ (override));
+ MOCK_METHOD(IBinder*, onAsBinder, (), (override));
+};
+
+class NativeThermalUnitTest : public Test {
+public:
+ void SetUp() override {
+ mMockIThermalService = new StrictMock<MockIThermalService>();
+ AThermal_setIThermalServiceForTesting(mMockIThermalService);
+ mThermalManager = AThermal_acquireManager();
+ }
+
+ void TearDown() override {
+ AThermal_setIThermalServiceForTesting(nullptr);
+ AThermal_releaseManager(mThermalManager);
+ }
+
+ StrictMock<MockIThermalService>* mMockIThermalService = nullptr;
+ AThermalManager* mThermalManager = nullptr;
+};
+
+static void checkThermalHeadroomThresholds(const std::vector<float>& expected,
+ const AThermalHeadroomThreshold* thresholds,
+ size_t size) {
+ if (thresholds == nullptr) {
+ FAIL() << "Unexpected null thresholds pointer";
+ }
+ for (int i = 0; i < (int)size; i++) {
+ auto t = thresholds[i];
+ ASSERT_EQ(i, t.thermalStatus) << "threshold " << i << " should have status " << i;
+ ASSERT_EQ(expected[i], t.headroom)
+ << "threshold " << i << " should have headroom " << expected[i];
+ }
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholds) {
+ std::vector<float> expected = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(expected), Return(Status())));
+ const AThermalHeadroomThreshold* thresholds1 = nullptr;
+ size_t size1;
+ ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds1, &size1));
+ checkThermalHeadroomThresholds(expected, thresholds1, size1);
+ // following calls should be cached
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_)).Times(0);
+
+ const AThermalHeadroomThreshold* thresholds2 = nullptr;
+ size_t size2;
+ ASSERT_EQ(OK, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds2, &size2));
+ checkThermalHeadroomThresholds(expected, thresholds2, size2);
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithServerError) {
+ const AThermalHeadroomThreshold* thresholds = nullptr;
+ size_t size;
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillOnce(Return(
+ Status::fromExceptionCode(binder::Status::Exception::EX_ILLEGAL_ARGUMENT)));
+ ASSERT_EQ(EPIPE, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size));
+ ASSERT_EQ(nullptr, thresholds);
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithFeatureDisabled) {
+ const AThermalHeadroomThreshold* thresholds = nullptr;
+ size_t size;
+ EXPECT_CALL(*mMockIThermalService, getThermalHeadroomThresholds(_))
+ .Times(Exactly(1))
+ .WillOnce(Return(Status::fromExceptionCode(
+ binder::Status::Exception::EX_UNSUPPORTED_OPERATION)));
+ ASSERT_EQ(ENOSYS, AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, &size));
+ ASSERT_EQ(nullptr, thresholds);
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNullPtr) {
+ const AThermalHeadroomThreshold* thresholds = nullptr;
+ size_t size;
+ size_t* nullSize = nullptr;
+ ASSERT_EQ(EINVAL,
+ AThermal_getThermalHeadroomThresholds(mThermalManager, &thresholds, nullSize));
+ ASSERT_EQ(nullptr, thresholds);
+ ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, nullptr, &size));
+}
+
+TEST_F(NativeThermalUnitTest, TestGetThermalHeadroomThresholdsFailedWithNonEmptyPtr) {
+ const AThermalHeadroomThreshold* initialized = new AThermalHeadroomThreshold[1];
+ size_t size;
+ ASSERT_EQ(EINVAL, AThermal_getThermalHeadroomThresholds(mThermalManager, &initialized, &size));
+ delete[] initialized;
+}
diff --git a/native/android/tests/thermal/OWNERS b/native/android/tests/thermal/OWNERS
new file mode 100644
index 0000000..e3bbee92
--- /dev/null
+++ b/native/android/tests/thermal/OWNERS
@@ -0,0 +1 @@
+include /ADPF_OWNERS
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
index 1f6ef47..b43f2f16 100644
--- a/native/android/thermal.cpp
+++ b/native/android/thermal.cpp
@@ -16,27 +16,32 @@
#define LOG_TAG "thermal"
-#include <cerrno>
-#include <thread>
-#include <limits>
-
-#include <android/thermal.h>
+#include <android-base/thread_annotations.h>
#include <android/os/BnThermalStatusListener.h>
#include <android/os/IThermalService.h>
+#include <android/thermal.h>
#include <binder/IServiceManager.h>
+#include <thermal_private.h>
#include <utils/Log.h>
+#include <cerrno>
+#include <limits>
+#include <thread>
+
using android::sp;
using namespace android;
using namespace android::os;
struct ThermalServiceListener : public BnThermalStatusListener {
- public:
- virtual binder::Status onStatusChange(int32_t status) override;
- ThermalServiceListener(AThermalManager *manager) {mMgr = manager;}
- private:
- AThermalManager *mMgr;
+public:
+ virtual binder::Status onStatusChange(int32_t status) override;
+ ThermalServiceListener(AThermalManager *manager) {
+ mMgr = manager;
+ }
+
+private:
+ AThermalManager *mMgr;
};
struct ListenerCallback {
@@ -44,22 +49,29 @@
void* data;
};
+static IThermalService *gIThermalServiceForTesting = nullptr;
+
struct AThermalManager {
- public:
- static AThermalManager* createAThermalManager();
- AThermalManager() = delete;
- ~AThermalManager();
- status_t notifyStateChange(int32_t status);
- status_t getCurrentThermalStatus(int32_t *status);
- status_t addListener(AThermal_StatusCallback, void *data);
- status_t removeListener(AThermal_StatusCallback, void *data);
- status_t getThermalHeadroom(int32_t forecastSeconds, float *result);
- private:
- AThermalManager(sp<IThermalService> service);
- sp<IThermalService> mThermalSvc;
- sp<ThermalServiceListener> mServiceListener;
- std::vector<ListenerCallback> mListeners;
- std::mutex mMutex;
+public:
+ static AThermalManager *createAThermalManager();
+ AThermalManager() = delete;
+ ~AThermalManager();
+ status_t notifyStateChange(int32_t status);
+ status_t getCurrentThermalStatus(int32_t *status);
+ status_t addListener(AThermal_StatusCallback, void *data);
+ status_t removeListener(AThermal_StatusCallback, void *data);
+ status_t getThermalHeadroom(int32_t forecastSeconds, float *result);
+ status_t getThermalHeadroomThresholds(const AThermalHeadroomThreshold **, size_t *size);
+
+private:
+ AThermalManager(sp<IThermalService> service);
+ sp<IThermalService> mThermalSvc;
+ std::mutex mListenerMutex;
+ sp<ThermalServiceListener> mServiceListener GUARDED_BY(mListenerMutex);
+ std::vector<ListenerCallback> mListeners GUARDED_BY(mListenerMutex);
+ std::mutex mThresholdsMutex;
+ const AThermalHeadroomThreshold *mThresholds = nullptr; // GUARDED_BY(mThresholdsMutex)
+ size_t mThresholdsCount GUARDED_BY(mThresholdsMutex);
};
binder::Status ThermalServiceListener::onStatusChange(int32_t status) {
@@ -70,6 +82,9 @@
}
AThermalManager* AThermalManager::createAThermalManager() {
+ if (gIThermalServiceForTesting) {
+ return new AThermalManager(gIThermalServiceForTesting);
+ }
sp<IBinder> binder =
defaultServiceManager()->checkService(String16("thermalservice"));
@@ -81,12 +96,10 @@
}
AThermalManager::AThermalManager(sp<IThermalService> service)
- : mThermalSvc(service),
- mServiceListener(nullptr) {
-}
+ : mThermalSvc(std::move(service)), mServiceListener(nullptr) {}
AThermalManager::~AThermalManager() {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> listenerLock(mListenerMutex);
mListeners.clear();
if (mServiceListener != nullptr) {
@@ -94,10 +107,13 @@
mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
mServiceListener = nullptr;
}
+ listenerLock.unlock();
+ std::unique_lock<std::mutex> lock(mThresholdsMutex);
+ delete[] mThresholds;
}
status_t AThermalManager::notifyStateChange(int32_t status) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mListenerMutex);
AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
for (auto listener : mListeners) {
@@ -107,7 +123,7 @@
}
status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mListenerMutex);
if (callback == nullptr) {
// Callback can not be nullptr
@@ -141,7 +157,7 @@
}
status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock<std::mutex> lock(mListenerMutex);
auto it = std::remove_if(mListeners.begin(),
mListeners.end(),
@@ -198,6 +214,32 @@
return OK;
}
+status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result,
+ size_t *size) {
+ std::unique_lock<std::mutex> lock(mThresholdsMutex);
+ if (mThresholds == nullptr) {
+ auto thresholds = std::make_unique<std::vector<float>>();
+ binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get());
+ if (!ret.isOk()) {
+ if (ret.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
+ // feature is not enabled
+ return ENOSYS;
+ }
+ return EPIPE;
+ }
+ mThresholdsCount = thresholds->size();
+ auto t = new AThermalHeadroomThreshold[mThresholdsCount];
+ for (int i = 0; i < (int)mThresholdsCount; i++) {
+ t[i].headroom = (*thresholds)[i];
+ t[i].thermalStatus = static_cast<AThermalStatus>(i);
+ }
+ mThresholds = t;
+ }
+ *size = mThresholdsCount;
+ *result = mThresholds;
+ return OK;
+}
+
/**
* Acquire an instance of the thermal manager. This must be freed using
* {@link AThermal_releaseManager}.
@@ -291,14 +333,24 @@
* threshold. Returns NaN if the device does not support this functionality or if
* this function is called significantly faster than once per second.
*/
-float AThermal_getThermalHeadroom(AThermalManager *manager,
- int forecastSeconds) {
+float AThermal_getThermalHeadroom(AThermalManager *manager, int forecastSeconds) {
float result = 0.0f;
status_t ret = manager->getThermalHeadroom(forecastSeconds, &result);
-
if (ret != OK) {
result = std::numeric_limits<float>::quiet_NaN();
}
-
return result;
}
+
+int AThermal_getThermalHeadroomThresholds(AThermalManager *manager,
+ const AThermalHeadroomThreshold **outThresholds,
+ size_t *size) {
+ if (outThresholds == nullptr || *outThresholds != nullptr || size == nullptr) {
+ return EINVAL;
+ }
+ return manager->getThermalHeadroomThresholds(outThresholds, size);
+}
+
+void AThermal_setIThermalServiceForTesting(void *iThermalService) {
+ gIThermalServiceForTesting = static_cast<IThermalService *>(iThermalService);
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
index da5697d..77fb3e7 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
@@ -19,9 +19,14 @@
import androidx.navigation.NavController
fun NavController.navigateToLoading() {
- navigate(Screen.Loading.route)
+ navigateToAsRoot(Screen.Loading.route)
}
fun NavController.navigateToSinglePasswordScreen() {
- navigate(Screen.SinglePasswordScreen.route)
+ navigateToAsRoot(Screen.SinglePasswordScreen.route)
+}
+
+fun NavController.navigateToAsRoot(route: String) {
+ popBackStack()
+ navigate(route)
}
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 35f5772..b845c2b 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -86,6 +86,7 @@
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
+ android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
android:exported="false" />
<activity android:name=".InstallInstalling"
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index aa1fa16..811fa73 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -32,8 +32,8 @@
<item name="android:windowNoTitle">true</item>
</style>
- <style name="Theme.AlertDialogActivity.NoDim">
- <item name="android:windowNoTitle">true</item>
+ <style name="Theme.AlertDialogActivity.NoDim"
+ parent="@style/Theme.AlertDialogActivity.NoActionBar">
<item name="android:backgroundDimAmount">0</item>
</style>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 74f04e0..eef21991 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -36,12 +36,14 @@
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
-public class InstallFailed extends AlertActivity {
+public class InstallFailed extends Activity {
private static final String LOG_TAG = InstallFailed.class.getSimpleName();
/** Label of the app that failed to install */
private CharSequence mLabel;
+ private AlertDialog mDialog;
+
/**
* Unhide the appropriate label for the statusCode.
*
@@ -53,19 +55,19 @@
View viewToEnable;
switch (statusCode) {
case PackageInstaller.STATUS_FAILURE_BLOCKED:
- viewToEnable = requireViewById(R.id.install_failed_blocked);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_blocked);
break;
case PackageInstaller.STATUS_FAILURE_CONFLICT:
- viewToEnable = requireViewById(R.id.install_failed_conflict);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_conflict);
break;
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
- viewToEnable = requireViewById(R.id.install_failed_incompatible);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_incompatible);
break;
case PackageInstaller.STATUS_FAILURE_INVALID:
- viewToEnable = requireViewById(R.id.install_failed_invalid_apk);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed_invalid_apk);
break;
default:
- viewToEnable = requireViewById(R.id.install_failed);
+ viewToEnable = mDialog.requireViewById(R.id.install_failed);
break;
}
@@ -105,12 +107,18 @@
// Store label for dialog
mLabel = as.label;
- mAlert.setIcon(as.icon);
- mAlert.setTitle(as.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.done),
- (ignored, ignored2) -> finish(), null);
- setupAlert();
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setIcon(as.icon);
+ builder.setTitle(as.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setPositiveButton(getString(R.string.done),
+ (ignored, ignored2) -> finish());
+ builder.setOnCancelListener(dialog -> {
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
// Show out of space dialog if needed
if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 4992ef1..8d8254a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -19,6 +19,8 @@
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_APP_SNIPPET;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
+import android.app.Activity;
+import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
@@ -43,7 +45,7 @@
* <p>This has two phases: First send the data to the package manager, then wait until the package
* manager processed the result.</p>
*/
-public class InstallInstalling extends AlertActivity {
+public class InstallInstalling extends Activity {
private static final String LOG_TAG = InstallInstalling.class.getSimpleName();
private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";
@@ -67,6 +69,8 @@
/** The button that can cancel this dialog */
private Button mCancelButton;
+ private AlertDialog mDialog;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -90,10 +94,12 @@
PackageUtil.AppSnippet as = getIntent()
.getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
- mAlert.setIcon(as.icon);
- mAlert.setTitle(as.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setIcon(as.icon);
+ builder.setTitle(as.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setNegativeButton(getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
@@ -106,9 +112,11 @@
setResult(RESULT_CANCELED);
finish();
- }, null);
- setupAlert();
- requireViewById(R.id.installing).setVisibility(View.VISIBLE);
+ });
+ builder.setCancelable(false);
+ mDialog = builder.create();
+ mDialog.show();
+ mDialog.requireViewById(R.id.installing).setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
@@ -145,7 +153,7 @@
}
}
- mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
+ mCancelButton = mDialog.getButton(DialogInterface.BUTTON_NEGATIVE);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
index 483fb8c..cf2f85e 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -52,7 +52,7 @@
* If a package gets installed from a content URI this step stages the installation session
* reading bytes from the URI.
*/
-public class InstallStaging extends AlertActivity {
+public class InstallStaging extends Activity {
private static final String LOG_TAG = InstallStaging.class.getSimpleName();
private static final String STAGED_SESSION_ID = "STAGED_SESSION_ID";
@@ -65,6 +65,8 @@
/** The session the package is in */
private int mStagedSessionId;
+ private AlertDialog mDialog;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -72,10 +74,13 @@
mInstaller = getPackageManager().getPackageInstaller();
setFinishOnTouchOutside(true);
- mAlert.setIcon(R.drawable.ic_file_download);
- mAlert.setTitle(getString(R.string.app_name_unknown));
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setIcon(R.drawable.ic_file_download);
+ builder.setTitle(getString(R.string.app_name_unknown));
+ builder.setView(R.layout.install_content_view);
+ builder.setNegativeButton(getString(R.string.cancel),
(ignored, ignored2) -> {
if (mStagingTask != null) {
mStagingTask.cancel(true);
@@ -85,9 +90,21 @@
setResult(RESULT_CANCELED);
finish();
- }, null);
- setupAlert();
- requireViewById(R.id.staging).setVisibility(View.VISIBLE);
+ });
+ builder.setOnCancelListener(dialog -> {
+ if (mStagingTask != null) {
+ mStagingTask.cancel(true);
+ }
+
+ cleanupStagingSession();
+
+ setResult(RESULT_CANCELED);
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
+ mDialog.requireViewById(com.android.packageinstaller.R.id.staging)
+ .setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mStagedSessionId = savedInstanceState.getInt(STAGED_SESSION_ID, 0);
@@ -275,8 +292,9 @@
@Override
protected void onPreExecute() {
final long sizeBytes = getContentSizeBytes();
-
- mProgressBar = sizeBytes > 0 ? requireViewById(R.id.progress_indeterminate) : null;
+ if (sizeBytes > 0 && mDialog != null) {
+ mProgressBar = mDialog.requireViewById(R.id.progress_indeterminate);
+ }
if (mProgressBar != null) {
mProgressBar.setProgress(0);
mProgressBar.setMax(100);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index fbc9525..9af88c3b 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -17,6 +17,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -34,7 +35,7 @@
/**
* Finish installation: Return status code to the caller or display "success" UI to user
*/
-public class InstallSuccess extends AlertActivity {
+public class InstallSuccess extends Activity {
private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
@Nullable
@@ -46,6 +47,8 @@
@Nullable
private Intent mLaunchIntent;
+ private AlertDialog mDialog;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -83,20 +86,27 @@
return;
}
- mAlert.setIcon(mAppSnippet.icon);
- mAlert.setTitle(mAppSnippet.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
- null);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(mAppSnippet.icon);
+ builder.setTitle(mAppSnippet.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setPositiveButton(getString(R.string.launch), null);
+ builder.setNegativeButton(getString(R.string.done),
(ignored, ignored2) -> {
if (mAppPackageName != null) {
Log.i(LOG_TAG, "Finished installing " + mAppPackageName);
}
finish();
- }, null);
- setupAlert();
- requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
+ });
+ builder.setOnCancelListener(dialog -> {
+ if (mAppPackageName != null) {
+ Log.i(LOG_TAG, "Finished installing " + mAppPackageName);
+ }
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
+ mDialog.requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
// Enable or disable "launch" button
boolean enabled = false;
if (mLaunchIntent != null) {
@@ -107,7 +117,7 @@
}
}
- Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ Button launchButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
setResult(Activity.RESULT_OK, mLaunchIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index c5ae4a3..ceb580d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -71,7 +71,7 @@
* Based on the user response the package is then installed by launching InstallAppConfirm
* sub activity. All state transitions are handled in this activity
*/
-public class PackageInstallerActivity extends AlertActivity {
+public class PackageInstallerActivity extends Activity {
private static final String TAG = "PackageInstaller";
private static final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
@@ -135,11 +135,13 @@
// Would the mOk button be enabled if this activity would be resumed
private boolean mEnableOk = false;
+ private AlertDialog mDialog;
+
private void startInstallConfirm() {
TextView viewToEnable;
if (mAppInfo != null) {
- viewToEnable = requireViewById(R.id.install_confirm_question_update);
+ viewToEnable = mDialog.requireViewById(R.id.install_confirm_question_update);
final CharSequence existingUpdateOwnerLabel = getExistingUpdateOwnerLabel();
final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
@@ -157,7 +159,7 @@
}
} else {
// This is a new application with no permissions.
- viewToEnable = requireViewById(R.id.install_confirm_question);
+ viewToEnable = mDialog.requireViewById(R.id.install_confirm_question);
}
viewToEnable.setVisibility(View.VISIBLE);
@@ -480,10 +482,11 @@
}
private void bindUi() {
- mAlert.setIcon(mAppSnippet.icon);
- mAlert.setTitle(mAppSnippet.label);
- mAlert.setView(R.layout.install_content_view);
- mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setIcon(mAppSnippet.icon);
+ builder.setTitle(mAppSnippet.label);
+ builder.setView(R.layout.install_content_view);
+ builder.setPositiveButton(getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
@@ -493,20 +496,26 @@
startInstall();
}
}
- }, null);
- mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
+ });
+ builder.setNegativeButton(getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setActivityResult(RESULT_CANCELED);
finish();
- }, null);
- setupAlert();
+ });
+ builder.setOnCancelListener(dialog -> {
+ // Cancel and finish
+ setActivityResult(RESULT_CANCELED);
+ finish();
+ });
+ mDialog = builder.create();
+ mDialog.show();
- mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ mOk = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
if (!mOk.isInTouchMode()) {
- mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
+ mDialog.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
}
}
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
index 7c135a0..63efaf5 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
index 7b438c4..3ac1d0f 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
index ac64619..105d1a1 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
index 5506c8c..a038779 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
index f4b9063..a042ffb 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
index fc60c0f..ae0abdde 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_landscape_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
index 81a181f..ae11f81 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
index a620040..9bc2b5d 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
index 0b40aa8..fc845a6 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
index cfe8587..03db688 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
index 3632755..235df22 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
index 7e5b602..9044208 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/phone/light_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
index 8a0da31..8333e68 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_mainSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
index 236a1a0..fcfd1d8 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_preference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
index 72b7954..693c592 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_progressBar.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
index 36486c4..1345c37 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_slider.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
index fedce44..d0d014e 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_switchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
index 3b389d7..5bd1144 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
+++ b/packages/SettingsLib/Spa/screenshot/robotests/assets/tablet/dark_portrait_twoTargetSwitchPreference.png
Binary files differ
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index 3525037..5baf7be 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -16,20 +16,22 @@
package com.android.settingslib.spaprivileged.model.enterprise
-import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER
import android.app.admin.DevicePolicyResources.Strings.Settings.PRIVATE_CATEGORY_HEADER
import android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER
import android.content.Context
import android.content.pm.UserInfo
import com.android.settingslib.R
+import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
-class EnterpriseRepository(private val context: Context) {
- private val resources by lazy {
- checkNotNull(context.getSystemService(DevicePolicyManager::class.java)).resources
- }
+interface IEnterpriseRepository {
+ fun getEnterpriseString(updatableStringId: String, resId: Int): String
+}
- fun getEnterpriseString(updatableStringId: String, resId: Int): String =
+class EnterpriseRepository(private val context: Context) : IEnterpriseRepository {
+ private val resources by lazy { context.devicePolicyManager.resources }
+
+ override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
checkNotNull(resources.getString(updatableStringId) { context.getString(resId) })
fun getProfileTitle(userInfo: UserInfo): String = if (userInfo.isManagedProfile) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
new file mode 100644
index 0000000..3acc9ad
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 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.spaprivileged.model.enterprise
+
+import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.content.Context
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.widget.restricted.R
+
+sealed interface RestrictedMode
+
+data object NoRestricted : RestrictedMode
+
+data object BaseUserRestricted : RestrictedMode
+
+interface BlockedByAdmin : RestrictedMode {
+ fun getSummary(checked: Boolean?): String
+ fun sendShowAdminSupportDetailsIntent()
+}
+
+internal data class BlockedByAdminImpl(
+ private val context: Context,
+ private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin,
+ private val enterpriseRepository: IEnterpriseRepository = EnterpriseRepository(context),
+) : BlockedByAdmin {
+ override fun getSummary(checked: Boolean?) = when (checked) {
+ true -> enterpriseRepository.getEnterpriseString(
+ updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY,
+ resId = R.string.enabled_by_admin,
+ )
+
+ false -> enterpriseRepository.getEnterpriseString(
+ updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY,
+ resId = R.string.disabled_by_admin,
+ )
+
+ else -> ""
+ }
+
+ override fun sendShowAdminSupportDetailsIntent() {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 09cb98e..550966b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.spaprivileged.model.enterprise
-import android.app.admin.DevicePolicyResources.Strings.Settings
import android.content.Context
import android.os.UserHandle
import android.os.UserManager
@@ -25,55 +24,16 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.android.settingslib.RestrictedLockUtils
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
import com.android.settingslib.RestrictedLockUtilsInternal
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
-import com.android.settingslib.widget.restricted.R
data class Restrictions(
val userId: Int = UserHandle.myUserId(),
val keys: List<String>,
)
-sealed interface RestrictedMode
-
-data object NoRestricted : RestrictedMode
-
-data object BaseUserRestricted : RestrictedMode
-
-interface BlockedByAdmin : RestrictedMode {
- fun getSummary(checked: Boolean?): String
- fun sendShowAdminSupportDetailsIntent()
-}
-
-private data class BlockedByAdminImpl(
- private val context: Context,
- private val enforcedAdmin: EnforcedAdmin,
-) : BlockedByAdmin {
- private val enterpriseRepository by lazy { EnterpriseRepository(context) }
-
- override fun getSummary(checked: Boolean?) = when (checked) {
- true -> enterpriseRepository.getEnterpriseString(
- updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY,
- resId = R.string.enabled_by_admin,
- )
-
- false -> enterpriseRepository.getEnterpriseString(
- updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY,
- resId = R.string.disabled_by_admin,
- )
-
- else -> ""
- }
-
- override fun sendShowAdminSupportDetailsIntent() {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, enforcedAdmin)
- }
-}
-
interface RestrictionsProvider {
@Composable
fun restrictedModeState(): State<RestrictedMode?>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
index 7bd7fbe..06b3eab 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
@@ -16,9 +16,7 @@
package com.android.settingslib.spaprivileged.template.app
-import android.app.AppOpsManager.MODE_ALLOWED
-import android.app.AppOpsManager.MODE_DEFAULT
-import android.app.AppOpsManager.MODE_ERRORED
+import android.app.AppOpsManager
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
@@ -64,7 +62,7 @@
*/
open val permissionHasAppOpFlag: Boolean = true
- open val modeForNotAllowed: Int = MODE_ERRORED
+ open val modeForNotAllowed: Int = AppOpsManager.MODE_ERRORED
/**
* Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed.
@@ -130,27 +128,14 @@
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) =
recordListFlow.filterItem(::isChangeable)
- /**
- * Defining the default behavior as permissible as long as the package requested this permission
- * (This means pre-M gets approval during install time; M apps gets approval during runtime).
- */
@Composable
- override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? {
- if (record.hasRequestBroaderPermission) {
- // Broader permission trumps the specific permission.
- return { true }
- }
-
- val mode = record.appOpsController.mode.observeAsState()
- return {
- when (mode.value) {
- null -> null
- MODE_ALLOWED -> true
- MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) }
- else -> false
- }
- }
- }
+ override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? =
+ isAllowed(
+ record = record,
+ appOpsController = record.appOpsController,
+ permission = permission,
+ packageManagers = packageManagers,
+ )
override fun isChangeable(record: AppOpPermissionRecord) =
record.hasRequestPermission &&
@@ -161,3 +146,33 @@
record.appOpsController.setAllowed(newAllowed)
}
}
+
+/**
+ * Defining the default behavior as permissible as long as the package requested this permission
+ * (This means pre-M gets approval during install time; M apps gets approval during runtime).
+ */
+@Composable
+internal fun isAllowed(
+ record: AppOpPermissionRecord,
+ appOpsController: IAppOpsController,
+ permission: String,
+ packageManagers: IPackageManagers = PackageManagers,
+): () -> Boolean? {
+ if (record.hasRequestBroaderPermission) {
+ // Broader permission trumps the specific permission.
+ return { true }
+ }
+
+ val mode = appOpsController.mode.observeAsState()
+ return {
+ when (mode.value) {
+ null -> null
+ AppOpsManager.MODE_ALLOWED -> true
+ AppOpsManager.MODE_DEFAULT -> {
+ with(packageManagers) { record.app.hasGrantPermission(permission) }
+ }
+
+ else -> false
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 3380b7d..5655436 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -16,19 +16,16 @@
package com.android.settingslib.spaprivileged.template.app
-import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Bundle
import androidx.annotation.VisibleForTesting
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.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.os.bundleOf
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.SettingsEntry
@@ -49,6 +46,8 @@
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
internal class TogglePermissionAppInfoPageProvider(
private val appListTemplate: TogglePermissionAppListTemplate,
@@ -132,7 +131,7 @@
@VisibleForTesting
@Composable
-internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage(
+internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionAppInfoPage(
packageName: String,
userId: Int,
packageManagers: IPackageManagers = PackageManagers,
@@ -145,40 +144,34 @@
footerContent = { AnnotatedText(footerResId) },
packageManagers = packageManagers,
) {
- val model = createSwitchModel(checkNotNull(applicationInfo))
+ val app = applicationInfo ?: return@AppInfoPage
+ val record = rememberRecord(app).value ?: return@AppInfoPage
+ val isAllowed = isAllowed(record)
+ val isChangeable by rememberIsChangeable(record)
+ val switchModel = object : SwitchPreferenceModel {
+ override val title = stringResource(switchTitleResId)
+ override val checked = isAllowed
+ override val changeable = { isChangeable }
+ override val onCheckedChange: (Boolean) -> Unit = { setAllowed(record, it) }
+ }
val restrictions = Restrictions(userId, switchRestrictionKeys)
- RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory)
+ RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
}
}
@Composable
-private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel(
- app: ApplicationInfo,
-): TogglePermissionSwitchModel<T> {
- val context = LocalContext.current
- val record = remember(app) { transformItem(app) }
- val isAllowed = isAllowed(record)
- return remember(record) { TogglePermissionSwitchModel(context, this, record, isAllowed) }
- .also { model -> LaunchedEffect(model, Dispatchers.IO) { model.initState() } }
-}
+private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberRecord(app: ApplicationInfo) =
+ remember(app) {
+ flow {
+ emit(transformItem(app))
+ }.flowOn(Dispatchers.Default)
+ }.collectAsStateWithLifecycle(initialValue = null)
-private class TogglePermissionSwitchModel<T : AppRecord>(
- context: Context,
- private val listModel: TogglePermissionAppListModel<T>,
- private val record: T,
- isAllowed: () -> Boolean?,
-) : SwitchPreferenceModel {
- private var appChangeable by mutableStateOf(true)
- override val title: String = context.getString(listModel.switchTitleResId)
- override val checked = isAllowed
- override val changeable = { appChangeable }
-
- fun initState() {
- appChangeable = listModel.isChangeable(record)
- }
-
- override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
- listModel.setAllowed(record, newChecked)
- }
-}
+@Composable
+private fun <T : AppRecord> TogglePermissionAppListModel<T>.rememberIsChangeable(record: T) =
+ remember(record) {
+ flow {
+ emit(isChangeable(record))
+ }.flowOn(Dispatchers.Default)
+ }.collectAsStateWithLifecycle(initialValue = false)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
new file mode 100644
index 0000000..8fd16b3
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.spaprivileged.model.enterprise
+
+import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.RestrictedLockUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RestrictedModeTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val fakeEnterpriseRepository = object : IEnterpriseRepository {
+ override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
+ when (updatableStringId) {
+ Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY -> ENABLED_BY_ADMIN
+ Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY -> DISABLED_BY_ADMIN
+ else -> ""
+ }
+ }
+
+ @Test
+ fun blockedByAdmin_getSummaryWhenChecked() {
+ val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
+
+ val summary = blockedByAdmin.getSummary(true)
+
+ assertThat(summary).isEqualTo(ENABLED_BY_ADMIN)
+ }
+
+ @Test
+ fun blockedByAdmin_getSummaryNotWhenChecked() {
+ val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
+
+ val summary = blockedByAdmin.getSummary(false)
+
+ assertThat(summary).isEqualTo(DISABLED_BY_ADMIN)
+ }
+
+ private companion object {
+ const val RESTRICTION = "restriction"
+ val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin =
+ RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
+
+ const val ENABLED_BY_ADMIN = "Enabled by admin"
+ const val DISABLED_BY_ADMIN = "Disabled by admin"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
index 3b2fe0f..d158a24 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
@@ -32,44 +32,37 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class AppOpPermissionAppListTest {
- @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+ @get:Rule
+ val composeTestRule = createComposeRule()
- @get:Rule val composeTestRule = createComposeRule()
+ private val packageManagers = mock<IPackageManagers>()
- @Spy private val context: Context = ApplicationProvider.getApplicationContext()
+ private val appOpsManager = mock<AppOpsManager>()
- @Mock private lateinit var packageManagers: IPackageManagers
-
- @Mock private lateinit var appOpsManager: AppOpsManager
-
- @Mock private lateinit var packageManager: PackageManager
-
- private lateinit var listModel: TestAppOpPermissionAppListModel
-
- @Before
- fun setUp() {
- whenever(context.appOpsManager).thenReturn(appOpsManager)
- whenever(context.packageManager).thenReturn(packageManager)
- doNothing().whenever(packageManager)
- .updatePermissionFlags(any(), any(), any(), any(), any())
- listModel = TestAppOpPermissionAppListModel()
+ private val packageManager = mock<PackageManager> {
+ doNothing().whenever(mock).updatePermissionFlags(any(), any(), any(), any(), any())
}
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { appOpsManager } doReturn appOpsManager
+ on { packageManager } doReturn packageManager
+ }
+
+ private val listModel = TestAppOpPermissionAppListModel()
+
@Test
fun transformItem_recordHasCorrectApp() {
val record = listModel.transformItem(APP)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
index 8bfae14..270b3fa 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
@@ -38,44 +38,34 @@
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class TogglePermissionAppInfoPageTest {
@get:Rule
val composeTestRule = createComposeRule()
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
-
private val context: Context = ApplicationProvider.getApplicationContext()
- @Mock
- private lateinit var packageManagers: IPackageManagers
+ private val packageManagers = mock<IPackageManagers> {
+ on { getPackageInfoAsUser(PACKAGE_NAME, USER_ID) } doReturn PACKAGE_INFO
+ }
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
- private val fakeRestrictionsProvider = FakeRestrictionsProvider()
+ private val fakeRestrictionsProvider = FakeRestrictionsProvider().apply {
+ restrictedMode = NoRestricted
+ }
private val appListTemplate =
TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider))
private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate)
- @Before
- fun setUp() {
- fakeRestrictionsProvider.restrictedMode = NoRestricted
- whenever(packageManagers.getPackageInfoAsUser(PACKAGE_NAME, USER_ID))
- .thenReturn(PACKAGE_INFO)
- }
-
@Test
fun buildEntry() {
val entryList = appInfoPageProvider.buildEntry(null)
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ed03d94..0b0e063 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -325,6 +325,7 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
+ <uses-permission android:name="android.permission.QUARANTINE_APPS" />
<uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<!-- Permission needed to wipe the device for Test Harness Mode -->
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index cf51e21..f1ffa66 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -294,6 +294,8 @@
"tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
+ "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
+ "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
// Keyguard helper
@@ -616,6 +618,9 @@
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
+ plugins: [
+ "dagger2-compiler",
+ ],
}
// Opt-out config for optimizing the SystemUI target using R8.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5881631..e218308 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -982,6 +982,24 @@
android:excludeFromRecents="true"
android:visibleToInstantApps="true"/>
+ <activity android:name="com.android.systemui.communal.widgets.WidgetPickerActivity"
+ android:theme="@style/Theme.EditWidgetsActivity"
+ android:excludeFromRecents="true"
+ android:autoRemoveFromRecents="true"
+ android:showOnLockScreen="true"
+ android:launchMode="singleTop"
+ android:exported="false">
+ </activity>
+
+ <activity android:name="com.android.systemui.communal.widgets.EditWidgetsActivity"
+ android:theme="@style/Theme.EditWidgetsActivity"
+ android:excludeFromRecents="true"
+ android:autoRemoveFromRecents="true"
+ android:showOnLockScreen="true"
+ android:launchMode="singleTop"
+ android:exported="false">
+ </activity>
+
<!-- Doze with notifications, run in main sysui process for every user -->
<service
android:name=".doze.DozeService"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e340209..68d4b63 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -61,6 +61,13 @@
}
flag {
+ name: "notification_throttle_hun"
+ namespace: "systemui"
+ description: "During notification avalanche, throttle HUNs showing in fast succession."
+ bug: "307288824"
+}
+
+flag {
name: "scene_container"
namespace: "systemui"
description: "Enables the scene container framework go/flexiglass."
@@ -119,4 +126,18 @@
description: "Adds thread-local data to System UI's global coroutine scopes to "
"allow for tracing of coroutine continuations using System UI's tracinglib"
bug: "289353932"
+}
+
+flag {
+ name: "new_aod_transition"
+ namespace: "systemui"
+ description: "New LOCKSCREEN <=> AOD transition"
+ bug: "301915812"
+}
+
+flag {
+ name: "light_reveal_migration"
+ namespace: "systemui"
+ description: "Move LightRevealScrim to recommended architecture"
+ bug: "281655028"
}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 6e18cb9..c80902e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -45,6 +45,7 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
@@ -89,11 +90,8 @@
)
}
}
- IconButton(onClick = viewModel::onOpenWidgetPicker) {
- Icon(
- Icons.Default.Add,
- LocalContext.current.getString(R.string.button_to_open_widget_picker)
- )
+ IconButton(onClick = viewModel::onOpenWidgetEditor) {
+ Icon(Icons.Default.Add, stringResource(R.string.button_to_open_widget_editor))
}
// This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
diff --git a/packages/SystemUI/res/layout/edit_widgets.xml b/packages/SystemUI/res/layout/edit_widgets.xml
new file mode 100644
index 0000000..182e651
--- /dev/null
+++ b/packages/SystemUI/res/layout/edit_widgets.xml
@@ -0,0 +1,32 @@
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/edit_widgets"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ style="@android:Widget.DeviceDefault.Button.Colored"
+ android:id="@+id/add_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textSize="28sp"
+ android:text="@string/hub_mode_add_widget_button_text"/>
+
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/widget_picker.xml b/packages/SystemUI/res/layout/widget_picker.xml
new file mode 100644
index 0000000..827bd5d
--- /dev/null
+++ b/packages/SystemUI/res/layout/widget_picker.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/widgets_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="64dp"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3163533..daf6cb3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1056,10 +1056,12 @@
<!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
<string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
- <!-- Description for the button that opens the widget picker on click. [CHAR LIMIT=50] -->
- <string name="button_to_open_widget_picker">Open the widget picker</string>
+ <!-- Description for the button that opens the widget editor on click. [CHAR LIMIT=50] -->
+ <string name="button_to_open_widget_editor">Open the widget editor</string>
<!-- Description for the button that removes a widget on click. [CHAR LIMIT=50] -->
<string name="button_to_remove_widget">Remove a widget</string>
+ <!-- Text for the button that launches the hub mode widget picker. [CHAR LIMIT=50] -->
+ <string name="hub_mode_add_widget_button_text">Add Widget</string>
<!-- Related to user switcher --><skip/>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7ce530f..2117714 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -943,6 +943,11 @@
<item name="android:windowLightStatusBar">true</item>
</style>
+ <style name="Theme.EditWidgetsActivity"
+ parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
+ <item name="android:windowBackground">@android:color/white</item>
+ </style>
+
<style name="TextAppearance.Control">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 07359d1..175fcdb 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -26,7 +26,7 @@
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
+import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.annotation.SuppressLint;
@@ -395,7 +395,7 @@
mView.updateIcon(ICON_LOCK, true);
mView.setContentDescription(mLockedLabel);
mView.setVisibility(View.VISIBLE);
- } else if (mIsDozing && mFeatureFlags.isEnabled(NEW_AOD_TRANSITION)) {
+ } else if (mIsDozing && newAodTransition()) {
mView.animate()
.alpha(0f)
.setDuration(FADE_OUT_DURATION_MS)
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt
new file mode 100644
index 0000000..a068769
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/ScrimLogger.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 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.keyguard.logging
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.ScrimLog
+import com.google.errorprone.annotations.CompileTimeConstant
+import javax.inject.Inject
+
+/**
+ * A logger to log scrim state.
+ *
+ * To enable logcat echoing for this buffer use this command:
+ * ```
+ * $ adb shell cmd statusbar echo -b ScrimLog:VERBOSE
+ * ```
+ */
+class ScrimLogger
+@Inject
+constructor(
+ @ScrimLog val buffer: LogBuffer,
+) {
+ companion object {
+ val TAG = ScrimLogger::class.simpleName!!
+ }
+
+ fun d(
+ tag: String,
+ @CompileTimeConstant msg: String,
+ arg: Any,
+ ) = log("$tag::$TAG", LogLevel.DEBUG, msg, arg)
+
+ fun log(
+ tag: String,
+ level: LogLevel,
+ @CompileTimeConstant msg: String,
+ arg: Any,
+ ) =
+ buffer.log(
+ tag,
+ level,
+ {
+ str1 = msg
+ str2 = arg.toString()
+ },
+ { "$str1: $str2" }
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index f94f8c5..6345312 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -31,11 +31,11 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.res.R
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -191,7 +191,7 @@
// This code path is not used if the KeyguardTransitionRepository is managing the light
// reveal scrim.
- if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (!lightRevealMigration()) {
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim.revealAmount = 0f
@@ -210,7 +210,7 @@
}
override fun onKeyguardFadingAwayChanged() {
- if (featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 8f31a2d..a5bd89a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -48,9 +48,9 @@
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -237,7 +237,7 @@
)
}
REASON_AUTH_KEYGUARD -> {
- if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
// note: empty controller, currently shows no visual affordance
// instead SysUI will show the fingerprint icon in its DeviceEntryIconView
UdfpsBpViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 2c4ed58..35c3ded 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -184,6 +184,7 @@
}
init {
+ com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode()
view.repeatWhenAttached {
// repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
// can make the view not visible; and we still want to listen for events
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 6c45af2..3cdb573 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -36,3 +36,7 @@
override val contentDescription: ContentDescription?,
) : Icon()
}
+
+/** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */
+fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon =
+ Icon.Loaded(this, contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 273adcf..847b98e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -16,12 +16,17 @@
package com.android.systemui.communal.dagger
+import android.content.Context
import com.android.systemui.communal.data.db.CommunalDatabaseModule
import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
import com.android.systemui.communal.data.repository.CommunalRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
+import com.android.systemui.dagger.qualifiers.Application
import dagger.Module
+import dagger.Provides
@Module(
includes =
@@ -33,4 +38,11 @@
CommunalDatabaseModule::class,
]
)
-class CommunalModule
+class CommunalModule {
+ @Provides
+ fun provideEditWidgetsActivityStarter(
+ @Application context: Context
+ ): EditWidgetsActivityStarter {
+ return EditWidgetsActivityStarterImpl(context)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 771dfbc..7391a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -25,6 +25,7 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import javax.inject.Inject
@@ -48,6 +49,7 @@
smartspaceRepository: SmartspaceRepository,
tutorialInteractor: CommunalTutorialInteractor,
private val appWidgetHost: AppWidgetHost,
+ private val editWidgetsActivityStarter: EditWidgetsActivityStarter
) {
/** Whether communal features are enabled. */
@@ -72,6 +74,11 @@
communalRepository.setDesiredScene(newScene)
}
+ /** Show the widget editor Activity. */
+ fun showWidgetEditor() {
+ editWidgetsActivityStarter.startActivity()
+ }
+
/** Add a widget at the specified position. */
fun addWidget(componentName: ComponentName, priority: Int) =
widgetRepository.addWidget(componentName, priority)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 5efe6ce..14edc8e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.communal.ui.viewmodel
-import android.content.ComponentName
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
@@ -46,19 +45,6 @@
/** Delete a widget by id. */
fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
- /** Open the widget picker */
- fun onOpenWidgetPicker() {
- // STOPSHIP(b/306500486): refactor this when integrating with the widget picker.
- // Eventually clicking on this button will bring up the widget picker and inside
- // the widget picker, addWidget will be called to add the user selected widget.
- // For now, a stopwatch widget will be added to the end of the grid.
- communalInteractor.addWidget(
- componentName =
- ComponentName(
- "com.google.android.deskclock",
- "com.android.alarmclock.StopwatchAppWidgetProvider"
- ),
- priority = 0
- )
- }
+ /** Open the widget editor */
+ fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
new file mode 100644
index 0000000..78e85db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 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.widgets
+
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import androidx.activity.ComponentActivity
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** An Activity for editing the widgets that appear in hub mode. */
+class EditWidgetsActivity @Inject constructor(private val communalInteractor: CommunalInteractor) :
+ ComponentActivity() {
+ companion object {
+ /**
+ * Intent extra name for the {@link AppWidgetProviderInfo} of a widget to add to hub mode.
+ */
+ const val ADD_WIDGET_INFO = "add_widget_info"
+ private const val TAG = "EditWidgetsActivity"
+ }
+
+ private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
+ registerForActivityResult(StartActivityForResult()) { result ->
+ when (result.resultCode) {
+ RESULT_OK -> {
+ result.data
+ ?.let {
+ it.getParcelableExtra(
+ ADD_WIDGET_INFO,
+ AppWidgetProviderInfo::class.java
+ )
+ }
+ ?.let { communalInteractor.addWidget(it.provider, 0) }
+ ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+ }
+ else ->
+ Log.w(
+ TAG,
+ "Failed to receive result from widget picker, code=${result.resultCode}"
+ )
+ }
+ this@EditWidgetsActivity.finish()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setShowWhenLocked(true)
+ setContentView(R.layout.edit_widgets)
+
+ val addWidgetsButton = findViewById<View>(R.id.add_widget)
+ addWidgetsButton?.setOnClickListener({
+ addWidgetActivityLauncher.launch(
+ Intent(applicationContext, WidgetPickerActivity::class.java)
+ )
+ })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
new file mode 100644
index 0000000..846e300
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.widgets
+
+import android.content.Context
+import android.content.Intent
+import com.android.systemui.dagger.qualifiers.Application
+
+interface EditWidgetsActivityStarter {
+ fun startActivity()
+}
+
+class EditWidgetsActivityStarterImpl(@Application private val applicationContext: Context) :
+ EditWidgetsActivityStarter {
+ override fun startActivity() {
+ applicationContext.startActivity(
+ Intent(applicationContext, EditWidgetsActivity::class.java)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
new file mode 100644
index 0000000..3e6dbd5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetPickerActivity.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2023 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.widgets
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Intent
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.activity.ComponentActivity
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * An Activity responsible for displaying a list of widgets to add to the hub mode grid. This is
+ * essentially a placeholder until Launcher's widget picker can be used.
+ */
+class WidgetPickerActivity
+@Inject
+constructor(
+ private val appWidgetManager: AppWidgetManager,
+) : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.widget_picker)
+ setShowWhenLocked(true)
+
+ loadWidgets()
+ }
+
+ private fun loadWidgets() {
+ val containerView: ViewGroup? = findViewById(R.id.widgets_container)
+ containerView?.apply {
+ try {
+ appWidgetManager
+ .getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
+ ?.stream()
+ ?.limit(5)
+ ?.forEach { widgetInfo ->
+ val activity = this@WidgetPickerActivity
+ val widgetPreview =
+ widgetInfo.loadPreviewImage(activity, DisplayMetrics.DENSITY_HIGH)
+ val widgetView = ImageView(activity)
+ val lp = LinearLayout.LayoutParams(WIDGET_PREVIEW_SIZE, WIDGET_PREVIEW_SIZE)
+ widgetView.setLayoutParams(lp)
+ widgetView.setImageDrawable(widgetPreview)
+ widgetView.setOnClickListener({
+ setResult(
+ RESULT_OK,
+ Intent().putExtra(EditWidgetsActivity.ADD_WIDGET_INFO, widgetInfo)
+ )
+ finish()
+ })
+
+ addView(widgetView)
+ }
+ } catch (e: RuntimeException) {
+ Log.e(TAG, "Exception fetching widget providers", e)
+ }
+ }
+ }
+
+ companion object {
+ private const val WIDGET_PREVIEW_SIZE = 400
+ private const val TAG = "WidgetPickerActivity"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 32e40c9..4b27af1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,6 +19,8 @@
import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.communal.widgets.EditWidgetsActivity;
+import com.android.systemui.communal.widgets.WidgetPickerActivity;
import com.android.systemui.contrast.ContrastDialogActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
@@ -150,7 +152,17 @@
@ClassKey(SensorUseStartedActivity.class)
public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
+ /** Inject into EditWidgetsActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(EditWidgetsActivity.class)
+ public abstract Activity bindEditWidgetsActivity(EditWidgetsActivity activity);
+ /** Inject into WidgetPickerActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(WidgetPickerActivity.class)
+ public abstract Activity bindWidgetPickerActivity(WidgetPickerActivity activity);
/** Inject into SwitchToManagedProfileForCallActivity. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
new file mode 100644
index 0000000..b5d5803
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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.deviceentry.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the device entry udfps refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object DeviceEntryUdfpsRefactor {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.deviceEntryUdfpsRefactor()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
index dd58604..906896f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/ConditionalRestarter.kt
@@ -17,9 +17,9 @@
package com.android.systemui.flags
import android.util.Log
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.ConditionalRestarter.Condition
-import com.android.systemui.util.kotlin.UnflaggedApplication
-import com.android.systemui.util.kotlin.UnflaggedBackground
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Named
@@ -39,8 +39,8 @@
private val systemExitRestarter: SystemExitRestarter,
private val conditions: Set<@JvmSuppressWildcards Condition>,
@Named(RESTART_DELAY) private val restartDelaySec: Long,
- @UnflaggedApplication private val applicationScope: CoroutineScope,
- @UnflaggedBackground private val backgroundDispatcher: CoroutineContext,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineContext,
) : Restarter {
private var pendingReason = ""
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b5fe4c5..00f32ef 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -148,18 +148,6 @@
// TODO(b/255607168): Tracking Bug
@JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1")
- /**
- * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
- * new KeyguardTransitionRepository.
- */
- // TODO(b/281655028): Tracking bug
- @JvmField
- val LIGHT_REVEAL_MIGRATION = unreleasedFlag("light_reveal_migration", teamfood = true)
-
- // TODO(b/301915812): Tracking Bug
- @JvmField
- val NEW_AOD_TRANSITION = unreleasedFlag("new_aod_transition", teamfood = true)
-
// TODO(b/305984787):
@JvmField
val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
@@ -224,11 +212,6 @@
val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
unreleasedFlag("wallpaper_picker_grid_apply_button")
- /** Whether to run the new udfps keyguard refactor code. */
- // TODO(b/279440316): Tracking bug.
- @JvmField
- val REFACTOR_UDFPS_KEYGUARD_VIEWS = unreleasedFlag("refactor_udfps_keyguard_views")
-
/** Provide new auth messages on the bouncer. */
// TODO(b/277961132): Tracking bug.
@JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag("revamped_bouncer_messages")
@@ -285,11 +268,6 @@
R.bool.flag_stop_pulsing_face_scanning_animation,
"stop_pulsing_face_scanning_animation")
- /** Flag to use a separate view for the alternate bouncer. */
- // TODO(b/300440924): Tracking bug
- @JvmField
- val ALTERNATE_BOUNCER_VIEW: UnreleasedFlag = unreleasedFlag("alternate_bouncer_view")
-
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag("power_menu_lite")
@@ -614,10 +592,6 @@
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
- @JvmField
- val COROUTINE_TRACING =
- unreleasedFlag("coroutine_tracing")
-
// TODO(b/283071711): Tracking bug
@JvmField
val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
@@ -713,12 +687,6 @@
@JvmField
val USE_REPOS_FOR_BOUNCER_SHOWING = releasedFlag("use_repos_for_bouncer_showing")
- // 3100 - Haptic interactions
-
- // TODO(b/290213663): Tracking Bug
- @JvmField
- val ONE_WAY_HAPTICS_API_MIGRATION = releasedFlag("oneway_haptics_api_migration")
-
/** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
@JvmField
val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 1037b0e..017dac2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -31,8 +31,8 @@
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
@@ -134,7 +134,7 @@
val indicationArea = KeyguardIndicationArea(context, null)
keyguardIndicationController.setIndicationArea(indicationArea)
- if (!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
lockIconViewController.get().setLockIconView(LockIconView(context, null))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 54031dc..cb0f186 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -22,6 +22,7 @@
import android.graphics.Point
import androidx.core.animation.Animator
import androidx.core.animation.ValueAnimator
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -33,6 +34,8 @@
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.PowerButtonReveal
+import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -42,8 +45,6 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
-import kotlin.math.max
val DEFAULT_REVEAL_EFFECT = LiftReveal
@@ -72,8 +73,13 @@
keyguardRepository: KeyguardRepository,
val context: Context,
powerInteractor: PowerInteractor,
+ private val scrimLogger: ScrimLogger,
) : LightRevealScrimRepository {
+ companion object {
+ val TAG = LightRevealScrimRepository::class.simpleName!!
+ }
+
/** The reveal effect used if the device was locked/unlocked via the power button. */
private val powerButtonRevealEffect: Flow<LightRevealEffect?> =
flowOf(
@@ -120,25 +126,25 @@
/** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */
private val nonBiometricRevealEffect: Flow<LightRevealEffect?> =
- powerInteractor
- .detailedWakefulness
- .flatMapLatest { wakefulnessModel ->
- when {
- wakefulnessModel.isAwakeOrAsleepFrom(WakeSleepReason.POWER_BUTTON) ->
- powerButtonRevealEffect
- wakefulnessModel.isAwakeFrom(TAP) ->
- tapRevealEffect
- else ->
- flowOf(LiftReveal)
- }
- }
+ powerInteractor.detailedWakefulness.flatMapLatest { wakefulnessModel ->
+ when {
+ wakefulnessModel.isAwakeOrAsleepFrom(WakeSleepReason.POWER_BUTTON) ->
+ powerButtonRevealEffect
+ wakefulnessModel.isAwakeFrom(TAP) -> tapRevealEffect
+ else -> flowOf(LiftReveal)
+ }
+ }
private val revealAmountAnimator = ValueAnimator.ofFloat(0f, 1f).apply { duration = 500 }
override val revealAmount: Flow<Float> = callbackFlow {
val updateListener =
Animator.AnimatorUpdateListener {
- trySend((it as ValueAnimator).animatedValue as Float)
+ val value = (it as ValueAnimator).animatedValue
+ trySend(value as Float)
+ if (value <= 0.0f || value >= 1.0f) {
+ scrimLogger.d(TAG, "revealAmount", value)
+ }
}
revealAmountAnimator.addUpdateListener(updateListener)
awaitClose { revealAmountAnimator.removeUpdateListener(updateListener) }
@@ -146,6 +152,7 @@
override fun startRevealAmountAnimator(reveal: Boolean) {
if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse()
+ scrimLogger.d(TAG, "startRevealAmountAnimator, reveal", reveal)
}
override val revealEffect =
@@ -156,13 +163,21 @@
) { biometricUnlockState, biometricReveal, nonBiometricReveal ->
// Use the biometric reveal for any flavor of wake and unlocking.
- when (biometricUnlockState) {
- BiometricUnlockModel.WAKE_AND_UNLOCK,
- BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
- else -> nonBiometricReveal
- }
- ?: DEFAULT_REVEAL_EFFECT
+ val revealEffect =
+ when (biometricUnlockState) {
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
+ else -> nonBiometricReveal
+ }
+ ?: DEFAULT_REVEAL_EFFECT
+
+ scrimLogger.d(
+ TAG,
+ "revealEffect",
+ "$revealEffect, biometricUnlockState: ${biometricUnlockState.name}"
+ )
+ return@combine revealEffect
}
.distinctUntilChanged()
@@ -173,8 +188,7 @@
x,
y,
startRadius = 0,
- endRadius =
- max(max(x, display.width - x), max(y, display.height - y)),
+ endRadius = max(max(x, display.width - x), max(y, display.height - y)),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 6115d90..2d43897 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
@@ -37,6 +38,7 @@
private val transitionInteractor: KeyguardTransitionInteractor,
private val lightRevealScrimRepository: LightRevealScrimRepository,
@Application private val scope: CoroutineScope,
+ private val scrimLogger: ScrimLogger,
) {
init {
@@ -46,6 +48,7 @@
private fun listenForStartedKeyguardTransitionStep() {
scope.launch {
transitionInteractor.startedKeyguardTransitionStep.collect {
+ scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it)
if (willTransitionChangeEndState(it)) {
lightRevealScrimRepository.startRevealAmountAnimator(
willBeRevealedInState(it.to)
@@ -100,5 +103,7 @@
KeyguardState.OCCLUDED -> true
}
}
+
+ val TAG = LightRevealScrimInteractor::class.simpleName!!
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 41af9e8..cb5813e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -23,18 +23,17 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scrim.ScrimView
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.NotificationShadeWindowController
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
-import javax.inject.Inject
@ExperimentalCoroutinesApi
@SysUISingleton
@@ -42,13 +41,12 @@
@Inject
constructor(
private val notificationShadeWindowView: NotificationShadeWindowView,
- private val featureFlags: FeatureFlagsClassic,
private val alternateBouncerViewModel: AlternateBouncerViewModel,
@Application private val scope: CoroutineScope,
private val notificationShadeWindowController: NotificationShadeWindowController,
) : CoreStartable {
override fun start() {
- if (!featureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled) {
return
}
@@ -79,6 +77,7 @@
scope: CoroutineScope,
notificationShadeWindowController: NotificationShadeWindowController,
) {
+ DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
scope.launch {
// forcePluginOpen is necessary to show over occluded apps.
// This cannot be tied to the view's lifecycle because setting this allows the view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index a8b28bc..4b4a19e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -23,6 +23,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
@@ -51,6 +52,7 @@
bgViewModel: DeviceEntryBackgroundViewModel,
falsingManager: FalsingManager,
) {
+ DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
val longPressHandlingView = view.longPressHandlingView
val fgIconView = view.iconView
val bgView = view.bgView
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 4de9fc3..114fd94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -34,13 +34,13 @@
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.keyguard.KeyguardClockSwitch.MISSING_CLOCK_ID
import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -384,7 +384,7 @@
}
visibility = if (isVisible.value) View.VISIBLE else View.INVISIBLE
}
- featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> {
+ newAodTransition() -> {
animateInIconTranslation(statusViewMigrated)
if (isVisible.value) {
CrossFadeHelper.fadeIn(this, animatorListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index b7a165c..55df466 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -26,14 +26,13 @@
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
@@ -48,7 +47,6 @@
private val falsingManager: FalsingManager,
private val indicationController: KeyguardIndicationController,
private val vibratorHelper: VibratorHelper,
- private val featureFlags: FeatureFlagsClassic,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
if (keyguardBottomAreaRefactor()) {
@@ -86,11 +84,12 @@
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
- val lockIconViewId = if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
- R.id.device_entry_icon_view
- } else {
- R.id.lock_icon_view
- }
+ val lockIconViewId =
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
constraintSet.apply {
constrainWidth(R.id.start_button, width)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index 13ea8ff..790ddd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -31,6 +31,7 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -65,10 +66,7 @@
private val deviceEntryIconViewId = R.id.device_entry_icon_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (
- !keyguardBottomAreaRefactor() &&
- !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
- ) {
+ if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
return
}
@@ -77,7 +75,7 @@
}
val view =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
} else {
// keyguardBottomAreaRefactor()
@@ -87,7 +85,7 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let {
DeviceEntryIconViewBinder.bind(
it,
@@ -140,7 +138,7 @@
}
override fun removeViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
constraintLayout.removeView(deviceEntryIconViewId)
} else {
constraintLayout.removeView(R.id.lock_icon_view)
@@ -160,7 +158,7 @@
}
val iconId =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
deviceEntryIconViewId
} else {
R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 165ee36..7512e51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,6 +24,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
@@ -81,7 +82,7 @@
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
val lockId =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
R.id.device_entry_icon_view
} else {
R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 2c45da6..f2559ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -24,6 +24,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
@@ -81,7 +82,7 @@
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
val lockId =
- if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
R.id.device_entry_icon_view
} else {
R.id.lock_icon_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 60f75f0..68cb5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -21,11 +21,11 @@
import android.util.MathUtils
import android.view.View.VISIBLE
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -280,7 +280,7 @@
dozeParameters.displayNeedsBlanking -> false
// We only want the appear animations to happen when the notifications
// get fully hidden, since otherwise the un-hide animation overlaps.
- featureFlags.isEnabled(Flags.NEW_AOD_TRANSITION) -> true
+ newAodTransition() -> true
else -> fullyHidden
}
AnimatableEvent(fullyHidden, animate)
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 17ff1b1..0d81940 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -517,6 +517,16 @@
}
/**
+ * Provides a {@link LogBuffer} for Scrims like LightRevealScrim.
+ */
+ @Provides
+ @SysUISingleton
+ @ScrimLog
+ public static LogBuffer provideScrimLogBuffer(LogBufferFactory factory) {
+ return factory.create("ScrimLog", 100);
+ }
+
+ /**
* Provides a {@link LogBuffer} for dream-related logs.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt
new file mode 100644
index 0000000..e78a162
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/ScrimLog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 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.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for Scrims like LightRevealScrim */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class ScrimLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 78f2da5..789a1e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -279,6 +279,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ executeSetBindService(false);
+ }
+
+ @Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
handleDeath();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 771d07c..3afbd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -234,6 +234,9 @@
disabledByPolicy = viewModelState.enabledState == QSTileState.EnabledState.DISABLED
expandedAccessibilityClassName = viewModelState.expandedAccessibilityClassName
+ // Use LoopedAnimatable2DrawableWrapper to achieve animated tile icon
+ isTransient = false
+
when (viewModelState.sideViewIcon) {
is QSTileState.SideViewIcon.Custom -> {
sideViewCustomDrawable =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index d0f2784..cf1dfdc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -46,6 +46,7 @@
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
@@ -448,7 +449,7 @@
}
boolean bouncerShowing;
- if (mFeatureFlagsClassic.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
|| mAlternateBouncerInteractor.isVisibleState();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 37073a6..374e871 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -22,6 +22,7 @@
import android.view.LayoutInflater
import android.view.ViewStub
import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.biometrics.AuthRippleView
@@ -140,8 +141,14 @@
@SysUISingleton
fun providesLightRevealScrim(
notificationShadeWindowView: NotificationShadeWindowView,
+ scrimLogger: ScrimLogger,
): LightRevealScrim {
- return notificationShadeWindowView.requireViewById(R.id.light_reveal_scrim)
+ val scrim =
+ notificationShadeWindowView.requireViewById<LightRevealScrim>(
+ R.id.light_reveal_scrim
+ )
+ scrim.scrimLogger = scrimLogger
+ return scrim
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 3120128..39b7930 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -18,6 +18,7 @@
import android.view.View
import android.view.animation.PathInterpolator
import com.android.app.animation.Interpolators
+import com.android.keyguard.logging.ScrimLogger
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
@@ -89,7 +90,7 @@
}
}
-class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
+data class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
// Interpolator that reveals >80% of the content at 0.5 progress, makes revealing faster
private val interpolator =
@@ -155,7 +156,7 @@
}
}
-class CircleReveal(
+data class CircleReveal(
/** X-value of the circle center of the reveal. */
val centerX: Int,
/** Y-value of the circle center of the reveal. */
@@ -181,7 +182,7 @@
}
}
-class PowerButtonReveal(
+data class PowerButtonReveal(
/** Approximate Y-value of the center of the power button on the physical device. */
val powerButtonY: Float
) : LightRevealEffect {
@@ -253,7 +254,9 @@
) : View(context, attrs) {
/** Listener that is called if the scrim's opaqueness changes */
- lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
+ var isScrimOpaqueChangedListener: Consumer<Boolean>? = null
+
+ var scrimLogger: ScrimLogger? = null
/**
* How much of the underlying views are revealed, in percent. 0 means they will be completely
@@ -263,7 +266,9 @@
set(value) {
if (field != value) {
field = value
-
+ if (value <= 0.0f || value >= 1.0f) {
+ scrimLogger?.d(TAG, "revealAmount", "$value on ${logString()}")
+ }
revealEffect.setRevealAmountOnScrim(value, this)
updateScrimOpaque()
Trace.traceCounter(
@@ -285,6 +290,7 @@
field = value
revealEffect.setRevealAmountOnScrim(revealAmount, this)
+ scrimLogger?.d(TAG, "revealEffect", "$value on ${logString()}")
invalidate()
}
}
@@ -301,6 +307,7 @@
*/
internal var viewWidth: Int = initialWidth ?: 0
private set
+
internal var viewHeight: Int = initialHeight ?: 0
private set
@@ -342,7 +349,8 @@
private set(value) {
if (field != value) {
field = value
- isScrimOpaqueChangedListener.accept(field)
+ isScrimOpaqueChangedListener?.accept(field)
+ scrimLogger?.d(TAG, "isScrimOpaque", "$value on ${logString()}")
}
}
@@ -360,11 +368,13 @@
override fun setAlpha(alpha: Float) {
super.setAlpha(alpha)
+ scrimLogger?.d(TAG, "alpha", "$alpha on ${logString()}")
updateScrimOpaque()
}
override fun setVisibility(visibility: Int) {
super.setVisibility(visibility)
+ scrimLogger?.d(TAG, "visibility", "$visibility on ${logString()}")
updateScrimOpaque()
}
@@ -424,11 +434,7 @@
}
override fun onDraw(canvas: Canvas) {
- if (
- revealGradientWidth <= 0 ||
- revealGradientHeight <= 0 ||
- revealAmount == 0f
- ) {
+ if (revealGradientWidth <= 0 || revealGradientHeight <= 0 || revealAmount == 0f) {
if (revealAmount < 1f) {
canvas.drawColor(revealGradientEndColor)
}
@@ -461,4 +467,8 @@
PorterDuff.Mode.MULTIPLY
)
}
+
+ private fun logString(): String {
+ return this::class.simpleName!! + "@" + hashCode()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index a85c440..b1e52af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -244,7 +244,7 @@
}
// Add and bind.
- val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings
+ val toAdd: Sequence<String> = iconsDiff.added.asSequence() + failedBindings.toList()
for ((idx, notifKey) in toAdd.withIndex()) {
// Lookup the StatusBarIconView from the store.
val sbiv = viewStore.iconView(notifKey)
@@ -252,6 +252,7 @@
failedBindings.add(notifKey)
continue
}
+ failedBindings.remove(notifKey)
// The view might still be transiently added if it was just removed and added
// again
view.removeTransientView(sbiv)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index 2047c62..ee79727 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -73,6 +73,11 @@
override val uiEventId: UiEventEnum? = null,
override val eventLogData: EventLogData? = null
) : VisualInterruptionSuppressor {
+ constructor(
+ types: Set<VisualInterruptionType>,
+ reason: String
+ ) : this(types, reason, /* uiEventId = */ null)
+
/** @return true if these interruptions should be suppressed right now. */
abstract fun shouldSuppress(): Boolean
}
@@ -84,6 +89,11 @@
override val uiEventId: UiEventEnum? = null,
override val eventLogData: EventLogData? = null
) : VisualInterruptionSuppressor {
+ constructor(
+ types: Set<VisualInterruptionType>,
+ reason: String
+ ) : this(types, reason, /* uiEventId = */ null)
+
/**
* @param entry the notification to consider suppressing
* @return true if these interruptions should be suppressed for this notification right now
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 46488c6..38d782b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -20,6 +20,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
+import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.flags.Flags.UNCLEARED_TRANSIENT_HUN_FIX;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
@@ -202,9 +203,6 @@
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
private final RefactorFlag mAnimatedInsets;
-
- private final boolean mNewAodTransition;
-
private int mContentHeight;
private float mIntrinsicContentHeight;
private int mPaddingBetweenElements;
@@ -633,7 +631,6 @@
mIsSmallLandscapeLockscreenEnabled = mFeatureFlags.isEnabled(
Flags.LOCKSCREEN_ENABLE_LANDSCAPE);
mDebugLines = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
- mNewAodTransition = mFeatureFlags.isEnabled(Flags.NEW_AOD_TRANSITION);
mDebugRemoveAnimation = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
mAnimatedInsets =
@@ -1462,7 +1459,7 @@
@VisibleForTesting
public void updateStackHeight(float endHeight, float fraction) {
- if (!mNewAodTransition) {
+ if (!newAodTransition()) {
// During the (AOD<=>LS) transition where dozeAmount is changing,
// apply dozeAmount to stack height instead of expansionFraction
// to unfurl notifications on AOD=>LS wakeup (and furl up on LS=>AOD sleep)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index cbe9d4b..4e77801 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -49,7 +49,6 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
@@ -186,8 +185,6 @@
private long mLastFpFailureUptimeMillis;
private int mNumConsecutiveFpFailures;
- private final FeatureFlags mFeatureFlags;
-
private static final class PendingAuthenticated {
public final int userId;
public final BiometricSourceType biometricSourceType;
@@ -291,7 +288,6 @@
ScreenOffAnimationController screenOffAnimationController,
VibratorHelper vibrator,
SystemClock systemClock,
- FeatureFlags featureFlags,
DeviceEntryHapticsInteractor hapticsInteractor,
Lazy<SelectedUserInteractor> selectedUserInteractor,
BiometricUnlockInteractor biometricUnlockInteractor
@@ -322,7 +318,6 @@
mVibratorHelper = vibrator;
mLogger = biometricUnlockLogger;
mSystemClock = systemClock;
- mFeatureFlags = featureFlags;
mOrderUnlockAndWake = resources.getBoolean(
com.android.internal.R.bool.config_orderUnlockAndWake);
mHapticsInteractor = hapticsInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index cd7a9ea..4cb01e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -27,6 +27,7 @@
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+import static com.android.systemui.Flags.lightRevealMigration;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -129,6 +130,7 @@
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -238,6 +240,8 @@
import dalvik.annotation.optimization.NeverCompile;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
@@ -249,8 +253,6 @@
import javax.inject.Named;
import javax.inject.Provider;
-import dagger.Lazy;
-
/**
* A class handling initialization and coordination between some of the key central surfaces in
* System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -952,7 +954,7 @@
@Override
public void onKeyguardGoingAwayChanged() {
- if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
// This code path is not used if the KeyguardTransitionRepository is managing
// the lightreveal scrim.
return;
@@ -1221,7 +1223,7 @@
});
mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
- if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
LightRevealScrimViewBinder.bind(
mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
}
@@ -2354,7 +2356,7 @@
return;
}
- if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (lightRevealMigration()) {
return;
}
@@ -2777,7 +2779,7 @@
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if (!mFeatureFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (!DeviceEntryUdfpsRefactor.isEnabled()) {
if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
&& (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f)) {
@@ -3000,7 +3002,7 @@
return;
}
- if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ if (!lightRevealMigration()) {
mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
}
}
@@ -3155,7 +3157,7 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
+ if (!lightRevealMigration()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index 1f9952a..a62a1ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
+import static com.android.systemui.Flags.newAodTransition;
import android.content.Context;
import android.content.res.Resources;
@@ -106,9 +106,6 @@
private NotificationIconContainer mAodIcons;
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
-
- private final boolean mNewAodTransition;
-
private int mAodIconAppearTranslation;
private boolean mAnimationsEnabled;
@@ -145,7 +142,6 @@
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mNewAodTransition = featureFlags.isEnabled(NEW_AOD_TRANSITION);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
mDozeParameters = dozeParameters;
@@ -600,7 +596,7 @@
boolean animate = true;
if (!mBypassController.getBypassEnabled()) {
animate = mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking();
- if (!mNewAodTransition) {
+ if (!newAodTransition()) {
// We only want the appear animations to happen when the notifications get fully
// hidden, since otherwise the unhide animation overlaps
animate &= fullyHidden;
@@ -640,7 +636,7 @@
mAodIconsVisible = visible;
mAodIcons.animate().cancel();
if (animate) {
- if (mNewAodTransition) {
+ if (newAodTransition()) {
// Let's make sure the icon are translated to 0, since we cancelled it above
animateInAodIconTranslation();
if (mAodIconsVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 267b563..274b50f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -62,6 +62,7 @@
import com.android.systemui.bouncer.ui.BouncerView;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
@@ -1573,7 +1574,7 @@
* notification shade's child views.
*/
public boolean shouldInterceptTouchEvent(MotionEvent event) {
- if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
return false;
}
return mAlternateBouncerInteractor.isVisibleState();
@@ -1584,7 +1585,7 @@
* showing.
*/
public boolean onTouch(MotionEvent event) {
- if (mFlags.isEnabled(Flags.ALTERNATE_BOUNCER_VIEW)) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 07e2571..8e9c038 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -14,6 +14,9 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED;
import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -29,6 +32,8 @@
import android.util.Slog;
import android.view.View;
+import androidx.annotation.NonNull;
+
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.InitController;
import com.android.systemui.dagger.SysUISingleton;
@@ -54,7 +59,10 @@
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
@@ -63,6 +71,8 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.Set;
+
import javax.inject.Inject;
@SysUISingleton
@@ -163,7 +173,14 @@
initController.addPostInitTask(() -> {
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
- visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor);
+ if (VisualInterruptionRefactor.isEnabled()) {
+ visualInterruptionDecisionProvider.addCondition(mAlertsDisabledCondition);
+ visualInterruptionDecisionProvider.addCondition(mVrModeCondition);
+ visualInterruptionDecisionProvider.addFilter(mNeedsRedactionFilter);
+ visualInterruptionDecisionProvider.addCondition(mPanelsDisabledCondition);
+ } else {
+ visualInterruptionDecisionProvider.addLegacySuppressor(mInterruptSuppressor);
+ }
mLockscreenUserManager.setUpWithPresenter(this);
mGutsManager.setUpWithPresenter(
this, mNotifListContainer, mOnSettingsClickListener);
@@ -306,4 +323,54 @@
return !mNotificationAlertsInteractor.areNotificationAlertsEnabled();
}
};
+
+ private final VisualInterruptionCondition mAlertsDisabledCondition =
+ new VisualInterruptionCondition(Set.of(PEEK, PULSE, BUBBLE),
+ "notification alerts disabled") {
+ @Override
+ public boolean shouldSuppress() {
+ return !mNotificationAlertsInteractor.areNotificationAlertsEnabled();
+ }
+ };
+
+ private final VisualInterruptionCondition mVrModeCondition =
+ new VisualInterruptionCondition(Set.of(PEEK, BUBBLE), "device is in VR mode") {
+ @Override
+ public boolean shouldSuppress() {
+ return isDeviceInVrMode();
+ }
+ };
+
+ private final VisualInterruptionFilter mNeedsRedactionFilter =
+ new VisualInterruptionFilter(Set.of(PEEK), "needs redaction on public lockscreen") {
+ @Override
+ public boolean shouldSuppress(@NonNull NotificationEntry entry) {
+ if (!mKeyguardStateController.isOccluded()) {
+ return false;
+ }
+
+ if (!mLockscreenUserManager.needsRedaction(entry)) {
+ return false;
+ }
+
+ final int currentUserId = mLockscreenUserManager.getCurrentUserId();
+ final boolean currentUserPublic = mLockscreenUserManager.isLockscreenPublicMode(
+ currentUserId);
+
+ final int notificationUserId = entry.getSbn().getUserId();
+ final boolean notificationUserPublic =
+ mLockscreenUserManager.isLockscreenPublicMode(notificationUserId);
+
+ // TODO(b/135046837): we can probably relax this with dynamic privacy
+ return currentUserPublic || notificationUserPublic;
+ }
+ };
+
+ private final VisualInterruptionCondition mPanelsDisabledCondition =
+ new VisualInterruptionCondition(Set.of(PEEK), "disabled panel") {
+ @Override
+ public boolean shouldSuppress() {
+ return !mCommandQueue.panelsEnabled();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 2afb435..36a1e8a 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -49,6 +49,7 @@
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.concurrency.ThreadFactory
import com.android.app.tracing.traceSection
+import com.android.keyguard.logging.ScrimLogger
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
import java.util.concurrent.Executor
@@ -69,7 +70,8 @@
@Main private val executor: Executor,
private val threadFactory: ThreadFactory,
private val rotationChangeProvider: RotationChangeProvider,
- private val displayTracker: DisplayTracker
+ private val displayTracker: DisplayTracker,
+ private val scrimLogger: ScrimLogger,
) {
private val transitionListener = TransitionListener()
@@ -179,8 +181,8 @@
)
.apply {
revealEffect = createLightRevealEffect()
- isScrimOpaqueChangedListener = Consumer {}
revealAmount = calculateRevealAmount()
+ scrimLogger = this@UnfoldLightRevealOverlayAnimation.scrimLogger
}
newRoot.setView(newView, params)
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
new file mode 100644
index 0000000..a2a44e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapper.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 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.util.drawable
+
+import android.content.res.Resources
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.Drawable
+import androidx.appcompat.graphics.drawable.DrawableWrapperCompat
+
+/**
+ * Create a looped [Animatable2] restarting it when the animation finishes on its own. Calling
+ * [LoopedAnimatable2DrawableWrapper.stop] cancels further looping.
+ */
+class LoopedAnimatable2DrawableWrapper private constructor(private val animatable2: Animatable2) :
+ DrawableWrapperCompat(animatable2 as Drawable), Animatable2 {
+
+ private val loopedCallback = LoopedCallback()
+
+ override fun start() {
+ animatable2.start()
+ animatable2.registerAnimationCallback(loopedCallback)
+ }
+
+ override fun stop() {
+ // stop looping if someone stops the animation
+ animatable2.unregisterAnimationCallback(loopedCallback)
+ animatable2.stop()
+ }
+
+ override fun isRunning(): Boolean = animatable2.isRunning
+
+ override fun registerAnimationCallback(callback: Animatable2.AnimationCallback) =
+ animatable2.registerAnimationCallback(callback)
+
+ override fun unregisterAnimationCallback(callback: Animatable2.AnimationCallback): Boolean =
+ animatable2.unregisterAnimationCallback(callback)
+
+ override fun clearAnimationCallbacks() = animatable2.clearAnimationCallbacks()
+
+ override fun getConstantState(): ConstantState? =
+ drawable!!.constantState?.let(LoopedAnimatable2DrawableWrapper::LoopedDrawableState)
+
+ companion object {
+
+ /**
+ * Creates [LoopedAnimatable2DrawableWrapper] from a [drawable]. The [drawable] should
+ * implement [Animatable2].
+ *
+ * It supports the following resource tags:
+ * - `<animated-image>`
+ * - `<animated-vector>`
+ */
+ fun fromDrawable(drawable: Drawable): LoopedAnimatable2DrawableWrapper {
+ require(drawable is Animatable2)
+ return LoopedAnimatable2DrawableWrapper(drawable)
+ }
+ }
+
+ private class LoopedCallback : Animatable2.AnimationCallback() {
+
+ override fun onAnimationEnd(drawable: Drawable?) {
+ (drawable as? Animatable2)?.start()
+ }
+ }
+
+ private class LoopedDrawableState(private val nestedState: ConstantState) : ConstantState() {
+
+ override fun newDrawable(): Drawable = fromDrawable(nestedState.newDrawable())
+
+ override fun newDrawable(res: Resources?): Drawable =
+ fromDrawable(nestedState.newDrawable(res))
+
+ override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable =
+ fromDrawable(nestedState.newDrawable(res, theme))
+
+ override fun canApplyTheme(): Boolean = nestedState.canApplyTheme()
+
+ override fun getChangingConfigurations(): Int = nestedState.changingConfigurations
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index 81737c7..cc9335e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -5,8 +5,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Tracing
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
+import com.android.systemui.Flags.coroutineTracing
import com.android.app.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
import com.android.app.tracing.TraceContextElement
import dagger.Module
@@ -15,32 +14,9 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import javax.inject.Qualifier
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
-/** Key associated with a [Boolean] flag that enables or disables the coroutine tracing feature. */
-@Qualifier
-annotation class CoroutineTracingEnabledKey
-
-/**
- * Same as [@Application], but does not make use of flags. This should only be used when early usage
- * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic].
- */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class UnflaggedApplication
-
-/**
- * Same as [@Background], but does not make use of flags. This should only be used when early usage
- * of [@Application] would introduce a circular dependency on [FeatureFlagsClassic].
- */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class UnflaggedBackground
-
/** Providers for various coroutines-related constructs. */
@Module
class CoroutinesModule {
@@ -53,11 +29,6 @@
@Provides
@SysUISingleton
- @UnflaggedApplication
- fun unflaggedApplicationScope(): CoroutineScope = CoroutineScope(Dispatchers.Main.immediate)
-
- @Provides
- @SysUISingleton
@Main
@Deprecated(
"Use @Main CoroutineContext instead",
@@ -98,28 +69,14 @@
return Dispatchers.IO + tracingCoroutineContext
}
- @Provides
- @UnflaggedBackground
- @SysUISingleton
- fun unflaggedBackgroundCoroutineContext(): CoroutineContext {
- return Dispatchers.IO
- }
-
@OptIn(ExperimentalCoroutinesApi::class)
@Provides
@Tracing
@SysUISingleton
- fun tracingCoroutineContext(
- @CoroutineTracingEnabledKey enableTracing: Boolean
- ): CoroutineContext = if (enableTracing) TraceContextElement() else EmptyCoroutineContext
-
- companion object {
- @[Provides CoroutineTracingEnabledKey]
- fun provideIsCoroutineTracingEnabledKey(featureFlags: FeatureFlagsClassic): Boolean {
- return if (featureFlags.isEnabled(Flags.COROUTINE_TRACING)) {
- coroutineTracingIsEnabled = true
- true
- } else false
- }
+ fun tracingCoroutineContext(): CoroutineContext {
+ return if (coroutineTracing()) {
+ coroutineTracingIsEnabled = true
+ TraceContextElement()
+ } else EmptyCoroutineContext
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2d95b09..f4122d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -50,8 +50,6 @@
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
@@ -87,8 +85,6 @@
@JvmField @Rule
var mockitoRule = MockitoJUnit.rule()
- private val featureFlags = FakeFeatureFlags()
-
@Mock
lateinit var callback: AuthDialogCallback
@Mock
@@ -135,7 +131,6 @@
@Before
fun setup() {
displayRepository = FakeDisplayRepository()
- featureFlags.set(Flags.ONE_WAY_HAPTICS_API_MIGRATION, false)
displayStateInteractor =
DisplayStateInteractorImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index d2b81e0..00ea78f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -17,14 +17,14 @@
package com.android.systemui.biometrics
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import dagger.BindsInstance
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index af4bf36..e0567a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -45,6 +46,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -59,6 +61,7 @@
private lateinit var widgetRepository: FakeCommunalWidgetRepository
private lateinit var smartspaceRepository: FakeSmartspaceRepository
private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
private lateinit var underTest: CommunalInteractor
@@ -76,6 +79,7 @@
widgetRepository = withDeps.widgetRepository
smartspaceRepository = withDeps.smartspaceRepository
keyguardRepository = withDeps.keyguardRepository
+ editWidgetsActivityStarter = withDeps.editWidgetsActivityStarter
underTest = withDeps.communalInteractor
}
@@ -322,4 +326,11 @@
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(true)
}
+
+ @Test
+ fun testShowWidgetEditorStartsActivity() =
+ testScope.runTest {
+ underTest.showWidgetEditor()
+ verify(editWidgetsActivityStarter).startActivity()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index 799bd5a..7242cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,15 +61,11 @@
MockitoAnnotations.initMocks(this)
fakeKeyguardRepository = FakeKeyguardRepository()
powerRepository = FakePowerRepository()
- powerInteractor = PowerInteractorFactory.create(
- repository = powerRepository
- ).powerInteractor
+ powerInteractor =
+ PowerInteractorFactory.create(repository = powerRepository).powerInteractor
- underTest = LightRevealScrimRepositoryImpl(
- fakeKeyguardRepository,
- context,
- powerInteractor,
- )
+ underTest =
+ LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context, powerInteractor, mock())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index b439fcf..722c11d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -18,9 +18,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index bc4c237..49f7565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -18,9 +18,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 03a1f7a..b483085 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -80,7 +81,8 @@
LightRevealScrimInteractor(
keyguardTransitionInteractor,
fakeLightRevealScrimRepository,
- testScope.backgroundScope
+ testScope.backgroundScope,
+ mock()
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 2dfc132..16d072e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -80,11 +80,7 @@
MockitoAnnotations.initMocks(this)
testScope = TestScope()
configRepository = FakeConfigurationRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
KeyguardInteractorFactory.create(featureFlags = featureFlags).let {
keyguardInteractor = it.keyguardInteractor
keyguardRepository = it.repository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index 570dfb3..e9399cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -18,7 +18,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 75bdcdd..a010ea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -67,10 +67,7 @@
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
featureFlags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
- set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
- }
+ FakeFeatureFlagsClassic().apply { set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false) }
underTest =
DefaultDeviceEntryIconSection(
keyguardUpdateMonitor,
@@ -98,7 +95,7 @@
@Test
fun addViewsConditionally_migrateAndRefactorFlagsOn() {
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -107,7 +104,7 @@
@Test
fun addViewsConditionally_migrateFlagOff() {
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isEqualTo(0)
@@ -115,7 +112,7 @@
@Test
fun applyConstraints_udfps_refactor_off() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.applyConstraints(cs)
@@ -127,7 +124,7 @@
@Test
fun applyConstraints_udfps_refactor_on() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.applyConstraints(cs)
@@ -139,7 +136,7 @@
@Test
fun testCenterIcon_udfps_refactor_off() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.centerIcon(Point(5, 6), 1F, cs)
@@ -155,7 +152,7 @@
@Test
fun testCenterIcon_udfps_refactor_on() {
- featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val cs = ConstraintSet()
underTest.centerIcon(Point(5, 6), 1F, cs)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 259c74f..a838684 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -21,18 +21,17 @@
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -46,6 +45,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.ClockController
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -64,7 +65,6 @@
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -109,10 +109,7 @@
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
- val featureFlags =
- FakeFeatureFlagsClassic().apply {
- set(Flags.FACE_AUTH_REFACTOR, true)
- }
+ val featureFlags = FakeFeatureFlagsClassic().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
keyguardInteractor = withDeps.keyguardInteractor
@@ -351,10 +348,7 @@
.create(
test = this,
featureFlags =
- FakeFeatureFlagsClassicModule {
- setDefault(Flags.NEW_AOD_TRANSITION)
- set(Flags.FACE_AUTH_REFACTOR, true)
- },
+ FakeFeatureFlagsClassicModule { set(Flags.FACE_AUTH_REFACTOR, true) },
mocks =
TestMocksModule(
dozeParameters = dozeParams,
@@ -367,6 +361,11 @@
block()
}
+ @Before
+ fun before() {
+ mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
+ }
+
@Test
fun iconContainer_isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = runTest {
val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index 4074851..c50be04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -18,15 +18,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -39,6 +37,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
@@ -77,15 +77,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 5c85357..26704da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,14 +18,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -35,13 +33,14 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -69,15 +68,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 4cbefa3d..ff3135a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,14 +18,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.collectValues
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
+import com.android.systemui.collectValues
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -35,6 +33,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
@@ -68,15 +68,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 4f56435..8afd8e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -18,13 +18,11 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -34,6 +32,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.collect.Range
@@ -69,15 +69,15 @@
mocks: TestMocksModule,
): TestComponent
}
+ }
- fun shadeExpanded(expanded: Boolean) {
- if (expanded) {
- shadeRepository.setQsExpansion(1f)
- } else {
- keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- shadeRepository.setQsExpansion(0f)
- shadeRepository.setLockscreenShadeExpansion(0f)
- }
+ private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ if (expanded) {
+ shadeRepository.setQsExpansion(1f)
+ } else {
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ shadeRepository.setQsExpansion(0f)
+ shadeRepository.setLockscreenShadeExpansion(0f)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 32acefe..5058b16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -67,11 +67,7 @@
overrideResource(com.android.systemui.res.R.dimen.lock_icon_padding, defaultPadding)
testScope = TestScope()
shadeRepository = FakeShadeRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
KeyguardInteractorFactory.create(
featureFlags = featureFlags,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index 4f970d7..f039f53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -75,11 +75,7 @@
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
fakeCommandQueue = FakeCommandQueue()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
bouncerRepository = FakeKeyguardBouncerRepository()
transitionRepository = FakeKeyguardTransitionRepository()
shadeRepository = FakeShadeRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index 30e4866..c1805db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -83,11 +83,7 @@
testScope = TestScope()
transitionRepository = FakeKeyguardTransitionRepository()
shadeRepository = FakeShadeRepository()
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
- set(Flags.FACE_AUTH_REFACTOR, false)
- }
+ featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) }
KeyguardInteractorFactory.create(
featureFlags = featureFlags,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 6cc52d7..fbd63c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -403,6 +403,31 @@
verify(falseContext).bindServiceAsUser(any(), any(), eq(flags), any());
}
+ @Test
+ public void testNullBindingCallsUnbind() {
+ Context mockContext = mock(Context.class);
+ // Binding has to succeed
+ when(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+ TileLifecycleManager manager = new TileLifecycleManager(mHandler, mockContext,
+ mock(IQSService.class),
+ mMockPackageManagerAdapter,
+ mMockBroadcastDispatcher,
+ mTileServiceIntent,
+ mUser,
+ mActivityManager,
+ mExecutor);
+
+ manager.executeSetBindService(true);
+ mExecutor.runAllReady();
+
+ ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+ verify(mockContext).bindServiceAsUser(any(), captor.capture(), anyInt(), any());
+
+ captor.getValue().onNullBinding(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ verify(mockContext).unbindService(captor.getValue());
+ }
+
private void mockChangeEnabled(long changeId, boolean enabled) {
doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
any(UserHandle.class)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
index ea8acc7..22fb152 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.viewmodel
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import com.android.settingslib.RestrictedLockUtils
import com.android.systemui.SysuiTestCase
@@ -45,8 +46,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
@@ -54,14 +53,15 @@
/** Tests all possible [QSTileUserAction]s. If you need */
@MediumTest
-@RunWith(Parameterized::class)
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class QSTileViewModelUserInputTest : SysuiTestCase() {
@Mock private lateinit var qsTileLogger: QSTileLogger
@Mock private lateinit var qsTileAnalytics: QSTileAnalytics
- @Parameter lateinit var userAction: QSTileUserAction
+ // TODO(b/299909989): this should be parametrised. b/299096521 blocks this.
+ private val userAction: QSTileUserAction = QSTileUserAction.Click(null)
private val tileConfig =
QSTileConfigTestBuilder.build { policy = QSTilePolicy.Restricted("test_restriction") }
@@ -180,15 +180,4 @@
testCoroutineDispatcher,
scope.backgroundScope,
)
-
- companion object {
-
- @JvmStatic
- @Parameterized.Parameters
- fun data(): Iterable<QSTileUserAction> =
- listOf(
- QSTileUserAction.Click(null),
- QSTileUserAction.LongClick(null),
- )
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2dd0af7..d89491c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -30,6 +30,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
@@ -51,7 +52,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.ALTERNATE_BOUNCER_VIEW
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES
import com.android.systemui.flags.Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
@@ -97,7 +97,6 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -112,8 +111,9 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -198,7 +198,7 @@
featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlagsClassic.set(REVAMPED_BOUNCER_MESSAGES, true)
featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- featureFlagsClassic.set(ALTERNATE_BOUNCER_VIEW, false)
+ mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
mCommunalRepository = FakeCommunalRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 4b62906..9c8816c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -29,7 +29,6 @@
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.SysuiTestCase
-import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
@@ -60,7 +59,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
@@ -114,8 +112,6 @@
@Mock private lateinit var centralSurfaces: CentralSurfaces
@Mock private lateinit var dozeServiceHost: DozeServiceHost
@Mock private lateinit var dozeScrimController: DozeScrimController
- @Mock private lateinit var backActionInteractor: BackActionInteractor
- @Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
@@ -192,7 +188,7 @@
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- featureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false)
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
testScope = TestScope()
controller =
NotificationShadeWindowViewController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index 09700e1..61e4370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -22,13 +22,11 @@
import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -45,6 +43,8 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
index f3c875e..92eb6ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorLegacyImplTest.kt
@@ -19,13 +19,11 @@
import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -35,6 +33,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.phone.DozeParameters
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
index 470e0c6..729f3f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -19,13 +19,11 @@
import android.content.pm.UserInfo
import android.os.UserManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -35,6 +33,8 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 43922e9..3efcf7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,8 +5,8 @@
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.SysUITestModule
-import com.android.TestMocksModule
+import com.android.systemui.SysUITestModule
+import com.android.systemui.TestMocksModule
import com.android.systemui.ExpandHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
@@ -611,9 +611,9 @@
@Component.Factory
interface Factory {
fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
+ @BindsInstance test: SysuiTestCase,
+ featureFlags: FakeFeatureFlagsClassicModule,
+ mocks: TestMocksModule,
): TestComponent
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
index d479937..f3094cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryTest.kt
@@ -17,13 +17,13 @@
package com.android.systemui.statusbar.notification.data.repository
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
index 707026e..b775079 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationAlertsInteractorTest.kt
@@ -16,8 +16,8 @@
import android.app.StatusBarManager
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
index bb6f1b6..bb3113a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsKeyguardInteractorTest.kt
@@ -14,20 +14,17 @@
package com.android.systemui.statusbar.notification.domain.interactor
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.notification.data.repository.FakeNotificationsKeyguardViewStateRepository
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Test
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 05deb1c..0341035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -17,14 +17,14 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.runTest
import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index c2c33de..10d4c62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -18,14 +18,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -39,6 +38,8 @@
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
@@ -94,7 +95,6 @@
FakeFeatureFlagsClassicModule {
setDefault(Flags.FACE_AUTH_REFACTOR)
set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
- setDefault(Flags.NEW_AOD_TRANSITION)
},
mocks =
TestMocksModule(
@@ -115,6 +115,7 @@
lastSleepReason = WakeSleepReason.OTHER,
)
}
+ mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index 87e9735..c2a1519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -20,14 +20,12 @@
import android.graphics.drawable.Icon
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
@@ -42,6 +40,8 @@
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 7423c2d..917569c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -19,16 +19,16 @@
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.runTest
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index e91d6d7..ba5ba2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -19,6 +19,7 @@
import static android.view.View.GONE;
import static android.view.WindowInsets.Type.ime;
+import static com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -162,7 +163,7 @@
// in the constructor.
mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
- mFeatureFlags.setDefault(Flags.NEW_AOD_TRANSITION);
+ mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION);
mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
// Inject dependencies before initializing the layout
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index db8f217..9c70c82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -19,13 +19,11 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.TestMocksModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
+import com.android.systemui.collectLastValue
import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
@@ -39,6 +37,8 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.res.R
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.user.domain.UserDomainLayerModule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 164325a..e61b4f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.google.common.truth.Truth.assertThat;
@@ -50,7 +49,6 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor;
@@ -130,14 +128,11 @@
@Mock
private BiometricUnlockInteractor mBiometricUnlockInteractor;
private final FakeSystemClock mSystemClock = new FakeSystemClock();
- private FakeFeatureFlags mFeatureFlags;
private BiometricUnlockController mBiometricUnlockController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
when(mKeyguardStateController.isFaceEnrolled()).thenReturn(true);
@@ -165,7 +160,6 @@
mAuthController, mStatusBarStateController,
mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
mSystemClock,
- mFeatureFlags,
mDeviceEntryHapticsInteractor,
() -> mSelectedUserInteractor,
mBiometricUnlockInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 251718d..ebdff08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -23,6 +23,7 @@
import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
+import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -349,11 +350,11 @@
mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
// For the Shade to animate during the Back gesture, we must enable the animation flag.
mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
- mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
+ mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
// Turn AOD on and toggle feature flag for jank fixes
mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 46b3996..225ddb6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -178,7 +178,7 @@
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, false);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
when(mNotificationShadeWindowController.getWindowRootView())
.thenReturn(mNotificationShadeWindowView);
@@ -771,7 +771,7 @@
mStatusBarKeyguardViewManager.addCallback(mCallback);
// GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// THEN the touch is not acted upon
@@ -781,7 +781,7 @@
@Test
public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
// GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
- mFeatureFlags.set(Flags.ALTERNATE_BOUNCER_VIEW, true);
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// THEN the touch is not intercepted
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 53c621d..bbdc9ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,22 +16,28 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK;
+import static com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.Flags;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
@@ -55,7 +61,10 @@
import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationAlertsInteractor;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionCondition;
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionFilter;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -64,10 +73,14 @@
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.util.List;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper()
@@ -76,18 +89,23 @@
private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider =
mock(VisualInterruptionDecisionProvider.class);
private NotificationInterruptSuppressor mInterruptSuppressor;
+ private VisualInterruptionCondition mAlertsDisabledCondition;
+ private VisualInterruptionCondition mVrModeCondition;
+ private VisualInterruptionFilter mNeedsRedactionFilter;
+ private VisualInterruptionCondition mPanelsDisabledCondition;
private CommandQueue mCommandQueue;
- private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
private final NotificationAlertsInteractor mNotificationAlertsInteractor =
mock(NotificationAlertsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
mock(KeyguardStateController.class);
- private final InitController mInitController = new InitController();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+ SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
@Before
public void setup() {
- mMetricsLogger = new FakeMetricsLogger();
mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
mDependency.injectTestDependency(StatusBarStateController.class,
mock(SysuiStatusBarStateController.class));
@@ -95,15 +113,182 @@
mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
- NotificationShadeWindowView notificationShadeWindowView =
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(true);
+ }
+
+ @Test
+ public void testInit_refactorDisabled() {
+ ensureRefactorDisabledState();
+ }
+
+ @Test
+ public void testInit_refactorEnabled() {
+ ensureRefactorEnabledState();
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_default_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_default_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ assertFalse(mAlertsDisabledCondition.shouldSuppress());
+ assertFalse(mVrModeCondition.shouldSuppress());
+ assertFalse(mNeedsRedactionFilter.shouldSuppress(createNotificationEntry()));
+ assertFalse(mAlertsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledStatusBar_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress heads up while disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledStatusBar_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress heads up while disabled",
+ mPanelsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledNotificationShade_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress interruptions while notification shade disabled",
+ mInterruptSuppressor.suppressAwakeHeadsUp(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressHeadsUp_disabledNotificationShade_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
+ false /* animate */);
+ TestableLooper.get(this).processAllMessages();
+
+ assertTrue("The panel should suppress interruptions while notification shade disabled",
+ mPanelsDisabledCondition.shouldSuppress());
+ }
+
+ @Test
+ public void testPanelsDisabledConditionSuppressesPeek() {
+ ensureRefactorEnabledState();
+
+ final Set<VisualInterruptionType> types = mPanelsDisabledCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertFalse(types.contains(BUBBLE));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(createFsiNotificationEntry()));
+ }
+
+ @Test
+ public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+ assertFalse(mNeedsRedactionFilter.shouldSuppress(createFsiNotificationEntry()));
+
+ final Set<VisualInterruptionType> types = mNeedsRedactionFilter.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertFalse(types.contains(BUBBLE));
+ }
+
+ @Test
+ public void testSuppressInterruptions_vrMode_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions",
+ mInterruptSuppressor.suppressAwakeInterruptions(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressInterruptions_vrMode_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ mStatusBarNotificationPresenter.mVrMode = true;
+
+ assertTrue("Vr mode should suppress interruptions", mVrModeCondition.shouldSuppress());
+
+ final Set<VisualInterruptionType> types = mVrModeCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertFalse(types.contains(PULSE));
+ assertTrue(types.contains(BUBBLE));
+ }
+
+ @Test
+ public void testSuppressInterruptions_statusBarAlertsDisabled_refactorDisabled() {
+ ensureRefactorDisabledState();
+
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
+
+ assertTrue("When alerts aren't enabled, interruptions are suppressed",
+ mInterruptSuppressor.suppressInterruptions(createNotificationEntry()));
+ }
+
+ @Test
+ public void testSuppressInterruptions_statusBarAlertsDisabled_refactorEnabled() {
+ ensureRefactorEnabledState();
+
+ when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
+
+ assertTrue("When alerts aren't enabled, interruptions are suppressed",
+ mAlertsDisabledCondition.shouldSuppress());
+
+ final Set<VisualInterruptionType> types = mAlertsDisabledCondition.getTypes();
+ assertTrue(types.contains(PEEK));
+ assertTrue(types.contains(PULSE));
+ assertTrue(types.contains(BUBBLE));
+ }
+
+ private void createPresenter() {
+ final ShadeViewController shadeViewController = mock(ShadeViewController.class);
+
+ final NotificationShadeWindowView notificationShadeWindowView =
mock(NotificationShadeWindowView.class);
+ when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
+
NotificationStackScrollLayoutController stackScrollLayoutController =
mock(NotificationStackScrollLayoutController.class);
when(stackScrollLayoutController.getView()).thenReturn(
mock(NotificationStackScrollLayout.class));
- when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
- ShadeViewController shadeViewController = mock(ShadeViewController.class);
+ final InitController initController = new InitController();
+
mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
mContext,
shadeViewController,
@@ -125,110 +310,76 @@
mock(NotifShadeEventSource.class),
mock(NotificationMediaManager.class),
mock(NotificationGutsManager.class),
- mInitController,
+ initController,
mVisualInterruptionDecisionProvider,
mock(NotificationRemoteInputManager.class),
mock(NotificationRemoteInputManager.Callback.class),
mock(NotificationListContainer.class));
- mInitController.executePostInitTasks();
- ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
+
+ initController.executePostInitTasks();
+ }
+
+ private void verifyAndCaptureSuppressors() {
+ mInterruptSuppressor = null;
+
+ final ArgumentCaptor<VisualInterruptionCondition> conditionCaptor =
+ ArgumentCaptor.forClass(VisualInterruptionCondition.class);
+ verify(mVisualInterruptionDecisionProvider, times(3)).addCondition(
+ conditionCaptor.capture());
+ final List<VisualInterruptionCondition> conditions = conditionCaptor.getAllValues();
+ mAlertsDisabledCondition = conditions.get(0);
+ mVrModeCondition = conditions.get(1);
+ mPanelsDisabledCondition = conditions.get(2);
+
+ final ArgumentCaptor<VisualInterruptionFilter> needsRedactionFilterCaptor =
+ ArgumentCaptor.forClass(VisualInterruptionFilter.class);
+ verify(mVisualInterruptionDecisionProvider).addFilter(needsRedactionFilterCaptor.capture());
+ mNeedsRedactionFilter = needsRedactionFilterCaptor.getValue();
+ }
+
+ private void verifyAndCaptureLegacySuppressor() {
+ mAlertsDisabledCondition = null;
+ mVrModeCondition = null;
+ mNeedsRedactionFilter = null;
+ mPanelsDisabledCondition = null;
+
+ final ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
verify(mVisualInterruptionDecisionProvider).addLegacySuppressor(suppressorCaptor.capture());
mInterruptSuppressor = suppressorCaptor.getValue();
}
- @Test
- public void testNoSuppressHeadsUp_default() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
+ private void ensureRefactorDisabledState() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR);
+ createPresenter();
+ verifyAndCaptureLegacySuppressor();
+ }
+
+ private void ensureRefactorEnabledState() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR);
+ createPresenter();
+ verifyAndCaptureSuppressors();
+ }
+
+ private NotificationEntry createNotificationEntry() {
+ return new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
- .setNotification(n)
+ .setNotification(new Notification.Builder(getContext(), "a").build())
.build();
-
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
- @Test
- public void testSuppressHeadsUp_disabledStatusBar() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
- mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress heads up while disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(entry));
- }
-
- @Test
- public void testSuppressHeadsUp_disabledNotificationShade() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
- mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
- false /* animate */);
- TestableLooper.get(this).processAllMessages();
-
- assertTrue("The panel should suppress interruptions while notification shade "
- + "disabled",
- mInterruptSuppressor.suppressAwakeHeadsUp(entry));
- }
-
- @Test
- public void testNoSuppressHeadsUp_FSI_nonOccludedKeyguard() {
- Notification n = new Notification.Builder(getContext(), "a")
+ private NotificationEntry createFsiNotificationEntry() {
+ final Notification notification = new Notification.Builder(getContext(), "a")
.setFullScreenIntent(mock(PendingIntent.class), true)
.build();
- NotificationEntry entry = new NotificationEntryBuilder()
+
+ return new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
- .setNotification(n)
+ .setNotification(notification)
.build();
-
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isOccluded()).thenReturn(false);
- assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
- }
-
- @Test
- public void testSuppressInterruptions_vrMode() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
- mStatusBarNotificationPresenter.mVrMode = true;
-
- assertTrue("Vr mode should suppress interruptions",
- mInterruptSuppressor.suppressAwakeInterruptions(entry));
- }
-
- @Test
- public void testSuppressInterruptions_statusBarAlertsDisabled() {
- Notification n = new Notification.Builder(getContext(), "a").build();
- NotificationEntry entry = new NotificationEntryBuilder()
- .setPkg("a")
- .setOpPkg("a")
- .setTag("a")
- .setNotification(n)
- .build();
- when(mNotificationAlertsInteractor.areNotificationAlertsEnabled()).thenReturn(false);
-
- assertTrue("When alerts aren't enabled, interruptions are suppressed",
- mInterruptSuppressor.suppressInterruptions(entry));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
index 78e7971..99e62ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -19,13 +19,13 @@
import android.app.NotificationManager.Policy
import android.provider.Settings
import androidx.test.filters.SmallTest
-import com.android.SysUITestComponent
-import com.android.SysUITestModule
-import com.android.collectLastValue
-import com.android.runCurrent
-import com.android.runTest
+import com.android.systemui.SysUITestComponent
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.collectLastValue
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.runCurrent
+import com.android.systemui.runTest
import com.android.systemui.statusbar.policy.data.repository.FakeZenModeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
new file mode 100644
index 0000000..6d2f00d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 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.util.drawable
+
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.Drawable
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+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.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class LoopedAnimatable2DrawableWrapperTest : SysuiTestCase() {
+
+ @Mock private lateinit var drawable: AnimatedDrawable
+ @Captor private lateinit var callbackCaptor: ArgumentCaptor<Animatable2.AnimationCallback>
+
+ private lateinit var underTest: LoopedAnimatable2DrawableWrapper
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = LoopedAnimatable2DrawableWrapper.fromDrawable(drawable)
+ }
+
+ @Test
+ fun startAddsTheCallback() {
+ underTest.start()
+
+ verify(drawable).registerAnimationCallback(any())
+ }
+
+ @Test
+ fun stopRemovesTheCallback() {
+ underTest.stop()
+
+ verify(drawable).unregisterAnimationCallback(any())
+ }
+
+ @Test
+ fun animationLooped() {
+ underTest.start()
+ verify(drawable).registerAnimationCallback(capture(callbackCaptor))
+
+ callbackCaptor.value.onAnimationEnd(drawable)
+
+ // underTest.start() + looped start()
+ verify(drawable, times(2)).start()
+ }
+
+ private abstract class AnimatedDrawable : Drawable(), Animatable2
+}
diff --git a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
index 360aa0f..de310b4 100644
--- a/packages/SystemUI/tests/src/com/android/CoroutineTestScopeModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/CoroutineTestScopeModule.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/SysUITestModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
index d8e7cd6..d0c1267 100644
--- a/packages/SystemUI/tests/src/com/android/SysUITestModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysUITestModule.kt
@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import android.content.Context
import android.content.res.Resources
import android.testing.TestableContext
import android.testing.TestableResources
-import com.android.systemui.FakeSystemUiModule
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/TestMocksModule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index fd50f15..37a4f61 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android
+package com.android.systemui
import android.app.ActivityManager
import android.app.admin.DevicePolicyManager
@@ -24,7 +24,6 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index 0c821ea..3aee889 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -22,6 +22,7 @@
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
@@ -40,6 +41,7 @@
smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
appWidgetHost: AppWidgetHost = mock(),
+ editWidgetsActivityStarter: EditWidgetsActivityStarter = mock(),
): WithDependencies {
val withDeps =
CommunalTutorialInteractorFactory.create(
@@ -57,6 +59,7 @@
withDeps.keyguardInteractor,
withDeps.communalTutorialInteractor,
appWidgetHost,
+ editWidgetsActivityStarter,
CommunalInteractor(
communalRepository,
widgetRepository,
@@ -64,6 +67,7 @@
smartspaceRepository,
withDeps.communalTutorialInteractor,
appWidgetHost,
+ editWidgetsActivityStarter,
),
)
}
@@ -78,6 +82,7 @@
val keyguardInteractor: KeyguardInteractor,
val tutorialInteractor: CommunalTutorialInteractor,
val appWidgetHost: AppWidgetHost,
+ val editWidgetsActivityStarter: EditWidgetsActivityStarter,
val communalInteractor: CommunalInteractor,
)
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index fc4ed1d..b9e34ee 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -33,3 +33,10 @@
],
visibility: ["//visibility:public"],
}
+
+java_host_for_device {
+ name: "core-xml-for-device",
+ libs: [
+ "core-xml-for-host",
+ ],
+}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 692d598..aa2d470 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -103,7 +103,33 @@
# Containers
class android.os.BaseBundle stubclass
class android.os.Bundle stubclass
+class android.os.PersistableBundle stubclass
# Misc
class android.os.PatternMatcher stubclass
class android.os.ParcelUuid stubclass
+
+# XML
+class com.android.internal.util.XmlPullParserWrapper stubclass
+class com.android.internal.util.XmlSerializerWrapper stubclass
+class com.android.internal.util.XmlUtils stubclass
+
+class com.android.modules.utils.BinaryXmlPullParser stubclass
+class com.android.modules.utils.BinaryXmlSerializer stubclass
+class com.android.modules.utils.FastDataInput stubclass
+class com.android.modules.utils.FastDataOutput stubclass
+class com.android.modules.utils.ModifiedUtf8 stubclass
+class com.android.modules.utils.TypedXmlPullParser stubclass
+class com.android.modules.utils.TypedXmlSerializer stubclass
+
+# Uri
+class android.net.Uri stubclass
+class android.net.UriCodec stubclass
+
+# Context: just enough to support wrapper, no further functionality
+class android.content.Context stub
+ method <init> ()V stub
+
+# Text
+class android.text.TextUtils stub
+ method isEmpty (Ljava/lang/CharSequence;)Z stub
diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
index 0aac084..edb0442 100644
--- a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
+++ b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
@@ -22,12 +22,30 @@
import java.lang.annotation.Target;
/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
+ * Test methods marked with this annotation are quietly ignored when running under a Ravenwood test
+ * environment. The test continues to execute normally under all other non-Ravenwood test
+ * environments.
+ *
+ * This annotation only takes effect when the containing class has a {@code
+ * RavenwoodRule} configured. Ignoring is accomplished by throwing an {@code org.junit
+ * .AssumptionViolatedException} which test infrastructure treats as being ignored.
+ *
+ * Developers are encouraged to use either the {@code blockedBy} and/or {@code reason} arguments
+ * to document why a test is being ignored, to aid in future audits of tests that are candidates
+ * to be enabled.
*
* @hide
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreUnderRavenwood {
+ /**
+ * One or more classes that aren't yet supported by Ravenwood, which this test depends on.
+ */
+ Class<?>[] blockedBy() default {};
+
+ /**
+ * General free-form description of why this test is being ignored.
+ */
+ String reason() default "";
}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index 776a19a..128155c 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -2,8 +2,36 @@
com.android.internal.util.ArrayUtils
+android.util.Xml
+
android.os.Binder
android.os.Binder$IdentitySupplier
android.os.IBinder
android.os.Process
android.os.SystemClock
+android.os.UserHandle
+
+android.content.ClipData
+android.content.ClipData$Item
+android.content.ClipDescription
+android.content.ComponentName
+android.content.ContentUris
+android.content.ContentValues
+android.content.Intent
+android.content.IntentFilter
+android.content.UriMatcher
+
+android.database.AbstractCursor
+android.database.CharArrayBuffer
+android.database.ContentObservable
+android.database.ContentObserver
+android.database.Cursor
+android.database.CursorIndexOutOfBoundsException
+android.database.CursorJoiner
+android.database.CursorWrapper
+android.database.DataSetObservable
+android.database.DataSetObserver
+android.database.MatrixCursor
+android.database.MatrixCursor$RowBuilder
+android.database.MergeCursor
+android.database.Observable
diff --git a/services/backup/Android.bp b/services/backup/Android.bp
index b086406..acb5911 100644
--- a/services/backup/Android.bp
+++ b/services/backup/Android.bp
@@ -19,5 +19,16 @@
defaults: ["platform_service_defaults"],
srcs: [":services.backup-sources"],
libs: ["services.core"],
- static_libs: ["app-compat-annotations"],
+ static_libs: ["app-compat-annotations", "backup_flags_lib"],
+}
+
+aconfig_declarations {
+ name: "backup_flags",
+ package: "com.android.server.backup",
+ srcs: ["flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "backup_flags_lib",
+ aconfig_declarations: "backup_flags",
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
new file mode 100644
index 0000000..d695d36
--- /dev/null
+++ b/services/backup/flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.server.backup"
+
+flag {
+ name: "enable_skipping_restore_launched_apps"
+ namespace: "onboarding"
+ description: "Enforce behavior determined by BackupTransport implementation on whether to skip "
+ "restore for apps that have been launched."
+ bug: "308401499"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 70d7fac..9f7b627 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IRestoreObserver;
@@ -32,11 +33,15 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.backup.Flags;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
@@ -296,12 +301,26 @@
return -1;
}
- private BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
+ @VisibleForTesting
+ BackupEligibilityRules getBackupEligibilityRules(RestoreSet restoreSet) {
// TODO(b/182986784): Remove device name comparison once a designated field for operation
// type is added to RestoreSet object.
int backupDestination = DEVICE_NAME_FOR_D2D_SET.equals(restoreSet.device)
? BackupDestination.DEVICE_TRANSFER : BackupDestination.CLOUD;
- return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
+
+ if (!Flags.enableSkippingRestoreLaunchedApps()) {
+ return mBackupManagerService.getEligibilityRulesForOperation(backupDestination);
+ }
+
+ boolean skipRestoreForLaunchedApps = (restoreSet.backupTransportFlags
+ & BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS) != 0;
+
+ return new BackupEligibilityRules(mBackupManagerService.getPackageManager(),
+ LocalServices.getService(PackageManagerInternal.class),
+ mUserId,
+ mBackupManagerService.getContext(),
+ backupDestination,
+ skipRestoreForLaunchedApps);
}
public synchronized int restorePackage(String packageName, IRestoreObserver observer,
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index bbec79d..96a873e 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -63,6 +63,7 @@
import com.android.server.backup.BackupAndRestoreFeatureFlags;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
+import com.android.server.backup.Flags;
import com.android.server.backup.OperationStorage;
import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.PackageManagerBackupAgent;
@@ -263,7 +264,14 @@
continue;
}
- if (backupEligibilityRules.appIsEligibleForBackup(info.applicationInfo)) {
+
+ ApplicationInfo applicationInfo = info.applicationInfo;
+ if (backupEligibilityRules.appIsEligibleForBackup(applicationInfo)) {
+ if (Flags.enableSkippingRestoreLaunchedApps()
+ && !backupEligibilityRules.isAppEligibleForRestore(applicationInfo)) {
+ continue;
+ }
+
mAcceptSet.add(info);
}
} catch (NameNotFoundException e) {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 7c47f1e..f24a3c1 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -80,6 +80,7 @@
private final int mUserId;
private boolean mIsProfileUser = false;
@BackupDestination private final int mBackupDestination;
+ private final boolean mSkipRestoreForLaunchedApps;
/**
* When this change is enabled, {@code adb backup} is automatically turned on for apps
@@ -112,12 +113,23 @@
int userId,
Context context,
@BackupDestination int backupDestination) {
+ this(packageManager, packageManagerInternal, userId, context, backupDestination,
+ /* skipRestoreForLaunchedApps */ false);
+ }
+
+ public BackupEligibilityRules(PackageManager packageManager,
+ PackageManagerInternal packageManagerInternal,
+ int userId,
+ Context context,
+ @BackupDestination int backupDestination,
+ boolean skipRestoreForLaunchedApps) {
mPackageManager = packageManager;
mPackageManagerInternal = packageManagerInternal;
mUserId = userId;
mBackupDestination = backupDestination;
UserManager userManager = context.getSystemService(UserManager.class);
mIsProfileUser = userManager.isProfile();
+ mSkipRestoreForLaunchedApps = skipRestoreForLaunchedApps;
}
/**
@@ -132,6 +144,9 @@
* <li>it is the special shared-storage backup package used for 'adb backup'
* </ol>
*
+ * These eligibility conditions are also checked before restore, in case the backup happened on
+ * a device / from the version of the app where these rules were not enforced.
+ *
* However, the above eligibility rules are ignored for non-system apps in in case of
* device-to-device migration, see {@link BackupDestination}.
*/
@@ -283,6 +298,27 @@
}
}
+ /**
+ * Determine if data restore should be run for the given package.
+ *
+ * <p>This is used in combination with {@link #appIsEligibleForBackup(ApplicationInfo)} that
+ * checks whether the backup being restored should have happened in the first place.</p>
+ */
+ public boolean isAppEligibleForRestore(ApplicationInfo app) {
+ if (!mSkipRestoreForLaunchedApps) {
+ return true;
+ }
+
+ // If an app implemented a BackupAgent, they are expected to handle being restored even
+ // after first launch and avoid conflicts between existing app data and restored data.
+ if (app.backupAgentName != null) {
+ return true;
+ }
+
+ // Otherwise only restore an app if it hasn't been launched before.
+ return !mPackageManagerInternal.wasPackageEverLaunched(app.packageName, mUserId);
+ }
+
/** Avoid backups of 'disabled' apps. */
@VisibleForTesting
boolean appIsDisabled(
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 22693ab..49457fb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -199,7 +199,7 @@
"biometrics_flags_lib",
"am_flags_lib",
"com_android_wm_shell_flags_lib",
- "android.app.flags-aconfig-java"
+ "service-jobscheduler-deviceidle.flags-aconfig-java",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a95ddf3..9d8f979 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -4157,7 +4157,7 @@
pw.println(" -D: enable debugging");
pw.println(" --suspend: debugged app suspend threads at startup (only with -D)");
pw.println(" -N: enable native debugging");
- pw.println(" -W: wait for launch to complete");
+ pw.println(" -W: wait for launch to complete (initial display)");
pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples (use with --start-profiler)");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f9fc4d4..36356bd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -111,6 +111,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
@@ -475,7 +476,12 @@
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- nms.registerObserver(mActivityChangeObserver);
+ if (!SdkLevel.isAtLeastV()) {
+ // On V+ devices, ConnectivityService calls BatteryStats API to update
+ // RadioPowerState change. So BatteryStatsService registers the callback only on
+ // pre V devices.
+ nms.registerObserver(mActivityChangeObserver);
+ }
cm.registerDefaultNetworkCallback(mNetworkCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ddccce5..b303346 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1829,7 +1829,7 @@
// screen on or animating, promote UI
state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP);
- } else {
+ } else if (!app.getWindowProcessController().isShowingUiWhileDozing()) {
// screen off, restrict UI scheduling
state.setCurProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
state.setCurrentSchedulingGroup(SCHED_GROUP_RESTRICTED);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 599d998..028be88 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -144,6 +144,7 @@
"haptics",
"hardware_backed_security_mainline",
"input",
+ "lse_desktop_experience",
"machine_learning",
"mainline_modularization",
"mainline_sdk",
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7292ea6..14aab13 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -531,19 +531,6 @@
}
}
- // Functions for uid mode access and manipulation.
- public SparseIntArray getNonDefaultUidModes() {
- return mAppOpsCheckingService.getNonDefaultUidModes(uid);
- }
-
- public int getUidMode(int op) {
- return mAppOpsCheckingService.getUidMode(uid, op);
- }
-
- public boolean setUidMode(int op, int mode) {
- return mAppOpsCheckingService.setUidMode(uid, op, mode);
- }
-
@SuppressWarnings("GuardedBy")
int evalMode(int op, int mode) {
return getUidStateTracker().evalMode(uid, op, mode);
@@ -613,15 +600,6 @@
this.packageName = packageName;
}
- @Mode int getMode() {
- return mAppOpsCheckingService.getPackageMode(packageName, this.op,
- UserHandle.getUserId(this.uid));
- }
- void setMode(@Mode int mode) {
- mAppOpsCheckingService.setPackageMode(packageName, this.op, mode,
- UserHandle.getUserId(this.uid));
- }
-
void removeAttributionsWithNoTime() {
for (int i = mAttributions.size() - 1; i >= 0; i--) {
if (!mAttributions.valueAt(i).hasAnyTime()) {
@@ -653,7 +631,11 @@
mAttributions.valueAt(i).createAttributedOpEntryLocked());
}
- return new OpEntry(op, getMode(), attributionEntries);
+ return new OpEntry(
+ op,
+ mAppOpsCheckingService.getPackageMode(
+ this.packageName, this.op, UserHandle.getUserId(this.uid)),
+ attributionEntries);
}
@NonNull OpEntry createSingleAttributionEntryLocked(@Nullable String attributionTag) {
@@ -668,7 +650,11 @@
}
}
- return new OpEntry(op, getMode(), attributionEntries);
+ return new OpEntry(
+ op,
+ mAppOpsCheckingService.getPackageMode(
+ this.packageName, this.op, UserHandle.getUserId(this.uid)),
+ attributionEntries);
}
boolean isRunning() {
@@ -1384,8 +1370,10 @@
}
final int code = foregroundOps.keyAt(fgi);
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
- && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ != AppOpsManager.opToDefaultMode(code)
+ && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChangedForAllPkgsInUid,
this, code, uidState.uid, true));
@@ -1405,7 +1393,11 @@
if (op == null) {
continue;
}
- if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
+ if (mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))
+ == AppOpsManager.MODE_FOREGROUND) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
this, listenerSet.valueAt(cbi), code, uidState.uid,
@@ -1497,7 +1489,7 @@
@Nullable
private ArrayList<AppOpsManager.OpEntry> collectUidOps(@NonNull UidState uidState,
@Nullable int[] ops) {
- final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ final SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
if (opModes == null) {
return null;
}
@@ -1778,7 +1770,11 @@
Ops ops = getOpsLocked(uid, packageName, null, false, null, /* edit */ false);
if (ops != null) {
ops.remove(op.op);
- op.setMode(AppOpsManager.opToDefaultMode(op.op));
+ mAppOpsCheckingService.setPackageMode(
+ packageName,
+ op.op,
+ AppOpsManager.opToDefaultMode(op.op),
+ UserHandle.getUserId(op.uid));
if (ops.size() <= 0) {
UidState uidState = ops.uidState;
ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
@@ -1848,15 +1844,16 @@
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
}
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
- previousMode = uidState.getUidMode(code);
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ != AppOpsManager.opToDefaultMode(code)) {
+ previousMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
} else {
// doesn't look right but is legacy behavior.
previousMode = MODE_DEFAULT;
}
mIgnoredCallback = permissionPolicyCallback;
- if (!uidState.setUidMode(code, mode)) {
+ if (!mAppOpsCheckingService.setUidMode(uidState.uid, code, mode)) {
return;
}
if (mode != MODE_ERRORED && mode != previousMode) {
@@ -2133,10 +2130,15 @@
synchronized (this) {
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ true);
if (op != null) {
- if (op.getMode() != mode) {
- previousMode = op.getMode();
+ if (mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid))
+ != mode) {
+ previousMode =
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid));
mIgnoredCallback = permissionPolicyCallback;
- op.setMode(mode);
+ mAppOpsCheckingService.setPackageMode(op.packageName, op.op, mode,
+ UserHandle.getUserId(op.uid));
}
}
}
@@ -2274,7 +2276,7 @@
for (int i = mUidStates.size() - 1; i >= 0; i--) {
UidState uidState = mUidStates.valueAt(i);
- SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ SparseIntArray opModes = mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
if (opModes != null && (uidState.uid == reqUid || reqUid == -1)) {
final int uidOpCount = opModes.size();
for (int j = uidOpCount - 1; j >= 0; j--) {
@@ -2283,7 +2285,7 @@
int previousMode = opModes.valueAt(j);
int newMode = isUidOpGrantedByRole(uidState.uid, code) ? MODE_ALLOWED :
AppOpsManager.opToDefaultMode(code);
- uidState.setUidMode(code, newMode);
+ mAppOpsCheckingService.setUidMode(uidState.uid, code, newMode);
for (String packageName : getPackagesForUid(uidState.uid)) {
callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
previousMode, mOpModeWatchers.get(code));
@@ -2325,14 +2327,22 @@
continue;
}
if (AppOpsManager.opAllowsReset(curOp.op)) {
- int previousMode = curOp.getMode();
+ int previousMode =
+ mAppOpsCheckingService.getPackageMode(
+ curOp.packageName,
+ curOp.op,
+ UserHandle.getUserId(curOp.uid));
int newMode = isPackageOpGrantedByRole(packageName, uidState.uid,
curOp.op) ? MODE_ALLOWED : AppOpsManager.opToDefaultMode(
curOp.op);
if (previousMode == newMode) {
continue;
}
- curOp.setMode(newMode);
+ mAppOpsCheckingService.setPackageMode(
+ curOp.packageName,
+ curOp.op,
+ newMode,
+ UserHandle.getUserId(curOp.uid));
changed = true;
uidChanged = true;
final int uid = curOp.uidState.uid;
@@ -2592,15 +2602,22 @@
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null
- && uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)) {
- final int rawMode = uidState.getUidMode(code);
+ && mAppOpsCheckingService.getUidMode(uidState.uid, code)
+ != AppOpsManager.opToDefaultMode(code)) {
+ final int rawMode = mAppOpsCheckingService.getUidMode(uidState.uid, code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, false, pvr.bypass, /* edit */ false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
- return raw ? op.getMode() : op.uidState.evalMode(op.op, op.getMode());
+ return raw
+ ? mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid))
+ : op.uidState.evalMode(
+ op.op,
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid)));
}
}
@@ -2836,8 +2853,11 @@
}
// 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.
- if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode =
+ uidState.evalMode(
+ code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
if (uidMode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -2850,7 +2870,13 @@
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ final int mode =
+ switchOp.uidState.evalMode(
+ switchOp.op,
+ mAppOpsCheckingService.getPackageMode(
+ switchOp.packageName,
+ switchOp.op,
+ UserHandle.getUserId(switchOp.uid)));
if (mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
@@ -3372,8 +3398,11 @@
final int switchCode = AppOpsManager.opToSwitch(code);
// 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.
- if (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode =
+ uidState.evalMode(
+ code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -3388,7 +3417,13 @@
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ final int mode =
+ switchOp.uidState.evalMode(
+ switchOp.op,
+ mAppOpsCheckingService.getPackageMode(
+ switchOp.packageName,
+ switchOp.op,
+ UserHandle.getUserId(switchOp.uid)));
if (mode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || mode != MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
@@ -3478,8 +3513,11 @@
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 (uidState.getUidMode(switchCode) != AppOpsManager.opToDefaultMode(switchCode)) {
- final int uidMode = uidState.evalMode(code, uidState.getUidMode(switchCode));
+ if (mAppOpsCheckingService.getUidMode(uidState.uid, switchCode)
+ != AppOpsManager.opToDefaultMode(switchCode)) {
+ final int uidMode =
+ uidState.evalMode(
+ code, mAppOpsCheckingService.getUidMode(uidState.uid, switchCode));
if (!shouldStartForMode(uidMode, startIfModeDefault)) {
if (DEBUG) {
Slog.d(TAG, "startOperation: uid reject #" + uidMode + " for code "
@@ -3491,7 +3529,13 @@
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
+ final int mode =
+ switchOp.uidState.evalMode(
+ switchOp.op,
+ mAppOpsCheckingService.getPackageMode(
+ switchOp.packageName,
+ switchOp.op,
+ UserHandle.getUserId(switchOp.uid)));
if (mode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || mode != MODE_DEFAULT)) {
if (DEBUG) {
@@ -5620,7 +5664,8 @@
}
for (int i=0; i<mUidStates.size(); i++) {
UidState uidState = mUidStates.valueAt(i);
- final SparseIntArray opModes = uidState.getNonDefaultUidModes();
+ final SparseIntArray opModes =
+ mAppOpsCheckingService.getNonDefaultUidModes(uidState.uid);
final ArrayMap<String, Ops> pkgOps = uidState.pkgOps;
if (dumpWatchers || dumpHistory) {
@@ -5648,7 +5693,12 @@
}
if (!hasMode) {
for (int opi = 0; !hasMode && opi < ops.size(); opi++) {
- if (ops.valueAt(opi).getMode() == dumpMode) {
+ final Op op = ops.valueAt(opi);
+ if (mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))
+ == dumpMode) {
hasMode = true;
}
}
@@ -5699,7 +5749,12 @@
if (dumpOp >= 0 && dumpOp != opCode) {
continue;
}
- if (dumpMode >= 0 && dumpMode != op.getMode()) {
+ if (dumpMode >= 0
+ && dumpMode
+ != mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))) {
continue;
}
if (!printedPackage) {
@@ -5707,14 +5762,25 @@
printedPackage = true;
}
pw.print(" "); pw.print(AppOpsManager.opToName(opCode));
- pw.print(" ("); pw.print(AppOpsManager.modeToName(op.getMode()));
+ pw.print(" (");
+ pw.print(
+ AppOpsManager.modeToName(
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName,
+ op.op,
+ UserHandle.getUserId(op.uid))));
final int switchOp = AppOpsManager.opToSwitch(opCode);
if (switchOp != opCode) {
pw.print(" / switch ");
pw.print(AppOpsManager.opToName(switchOp));
final Op switchObj = ops.get(switchOp);
- int mode = switchObj == null
- ? AppOpsManager.opToDefaultMode(switchOp) : switchObj.getMode();
+ int mode =
+ switchObj == null
+ ? AppOpsManager.opToDefaultMode(switchOp)
+ : mAppOpsCheckingService.getPackageMode(
+ switchObj.packageName,
+ switchObj.op,
+ UserHandle.getUserId(switchObj.uid));
pw.print("="); pw.print(AppOpsManager.modeToName(mode));
}
pw.println("): ");
@@ -5848,7 +5914,13 @@
for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
Ops ops = uidState.pkgOps.valueAt(pkgNum);
Op op = ops != null ? ops.get(code) : null;
- if (op == null || (op.getMode() != MODE_ALLOWED && op.getMode() != MODE_FOREGROUND)) {
+ if (op == null) {
+ continue;
+ }
+ final int mode =
+ mAppOpsCheckingService.getPackageMode(
+ op.packageName, op.op, UserHandle.getUserId(op.uid));
+ if (mode != MODE_ALLOWED && mode != MODE_FOREGROUND) {
continue;
}
int numAttrTags = op.mAttributions.size();
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a12243b..b0abf94 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2621,8 +2621,9 @@
*
* Callers are responsible for checking permissions if needed.
*/
- public void startLegacyVpnPrivileged(VpnProfile profile,
+ public void startLegacyVpnPrivileged(VpnProfile profileToStart,
@Nullable Network underlying, @NonNull LinkProperties egress) {
+ final VpnProfile profile = profileToStart.clone();
UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
@@ -3385,6 +3386,13 @@
* given network to start a new IKE session.
*/
private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
+ synchronized (Vpn.this) {
+ // Ignore stale runner.
+ if (mVpnRunner != this) return;
+ setVpnNetworkPreference(mSessionKey,
+ createUserAndRestrictedProfilesRanges(mUserId,
+ mConfig.allowedApplications, mConfig.disallowedApplications));
+ }
if (underlyingNetwork == null) {
// For null underlyingNetwork case, there will not be a NetworkAgent available so
// no underlying network update is necessary here. Note that updating
@@ -3905,6 +3913,7 @@
updateState(DetailedState.FAILED, exception.getMessage());
}
+ clearVpnNetworkPreference(mSessionKey);
disconnectVpnRunner();
}
@@ -4039,6 +4048,13 @@
}
resetIkeState();
+ if (errorCode != VpnManager.ERROR_CODE_NETWORK_LOST
+ // Clear the VPN network preference when the retry delay is higher than 5s.
+ // mRetryCount was increased when scheduleRetryNewIkeSession() is called,
+ // therefore use mRetryCount - 1 here.
+ && mDeps.getNextRetryDelayMs(mRetryCount - 1) > 5_000L) {
+ clearVpnNetworkPreference(mSessionKey);
+ }
}
/**
@@ -4085,13 +4101,17 @@
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
- clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
}
@Override
public void exitVpnRunner() {
+ // mSessionKey won't be changed since the Ikev2VpnRunner is created, so it's ok to use
+ // it outside the mExecutor. And clearing the VPN network preference here can prevent
+ // the case that the VPN network preference isn't cleared when Ikev2VpnRunner became
+ // stale.
+ clearVpnNetworkPreference(mSessionKey);
try {
mExecutor.execute(() -> {
disconnectVpnRunner();
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 1da7f0c..cd3f0f0 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.app.job.JobParameters;
import android.app.job.JobService;
+import android.content.pm.PackageManagerInternal;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
@@ -28,6 +29,7 @@
import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
public class SyncJobService extends JobService {
private static final String TAG = "SyncManager";
@@ -97,6 +99,20 @@
return true;
}
+ // TODO(b/209852664): remove this logic from here once it's added within JobScheduler.
+ // JobScheduler should not call onStartJob for syncs whose source packages are stopped.
+ // Until JS adds the relevant logic, this is a temporary solution to keep deferring syncs
+ // for packages in the stopped state.
+ if (android.content.pm.Flags.stayStopped()) {
+ if (LocalServices.getService(PackageManagerInternal.class)
+ .isPackageStopped(op.owningPackage, op.target.userId)) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, "Skipping sync for force-stopped package: " + op.owningPackage);
+ }
+ return false;
+ }
+ }
+
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
synchronized (sLock) {
final int jobId = params.getJobId();
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 3de188f..93d9b8d 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -411,6 +411,33 @@
}
/**
+ * Destroys ColorFade animation and its resources
+ *
+ * This method should be called when the ColorFade is no longer in use; i.e. when
+ * the {@link #mDisplayId display} has been removed.
+ */
+ public void destroy() {
+ if (DEBUG) {
+ Slog.d(TAG, "destroy");
+ }
+ if (mPrepared) {
+ if (mCreatedResources) {
+ attachEglContext();
+ try {
+ destroyScreenshotTexture();
+ destroyGLShaders();
+ destroyGLBuffers();
+ destroyEglSurface();
+ } finally {
+ detachEglContext();
+ }
+ }
+ destroyEglContext();
+ destroySurface();
+ }
+ }
+
+ /**
* Draws an animation frame showing the color fade activated at the
* specified level.
*
@@ -793,6 +820,12 @@
}
}
+ private void destroyEglContext() {
+ if (mEglDisplay != null && mEglContext != null) {
+ EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+ }
+ }
+
private static FloatBuffer createNativeFloatBuffer(int size) {
ByteBuffer bb = ByteBuffer.allocateDirect(size * 4);
bb.order(ByteOrder.nativeOrder());
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 8046dbf..11f4e5f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1607,6 +1607,19 @@
final long secondToken = Binder.clearCallingIdentity();
try {
final int displayId;
+ final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+ packageName, callingUid, virtualDisplayConfig);
+
+ if (virtualDisplayConfig.isHomeSupported()) {
+ if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) {
+ Slog.w(TAG, "Display created with home support but lacks "
+ + "VIRTUAL_DISPLAY_FLAG_TRUSTED, ignoring the home support request.");
+ } else {
+ mWindowManagerInternal.setHomeSupportedOnDisplay(displayUniqueId,
+ Display.TYPE_VIRTUAL, true);
+ }
+ }
+
synchronized (mSyncRoot) {
displayId =
createVirtualDisplayLocked(
@@ -1614,6 +1627,7 @@
projection,
callingUid,
packageName,
+ displayUniqueId,
virtualDevice,
surface,
flags,
@@ -1625,6 +1639,13 @@
}
}
+ if (displayId == Display.INVALID_DISPLAY && virtualDisplayConfig.isHomeSupported()
+ && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
+ // Failed to create the virtual display, so we should clean up the WM settings
+ // because it won't receive the onDisplayRemoved callback.
+ mWindowManagerInternal.clearDisplaySettings(displayUniqueId, Display.TYPE_VIRTUAL);
+ }
+
// Build a session describing the MediaProjection instance, if there is one. A session
// for a VirtualDisplay or physical display mirroring is handled in DisplayContent.
ContentRecordingSession session = null;
@@ -1698,6 +1719,7 @@
IMediaProjection projection,
int callingUid,
String packageName,
+ String uniqueId,
IVirtualDevice virtualDevice,
Surface surface,
int flags,
@@ -1710,10 +1732,9 @@
return -1;
}
-
Slog.d(TAG, "Virtual Display: creating DisplayDevice with VirtualDisplayAdapter");
DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked(
- callback, projection, callingUid, packageName, surface, flags,
+ callback, projection, callingUid, packageName, uniqueId, surface, flags,
virtualDisplayConfig);
if (device == null) {
Slog.w(TAG, "Virtual Display: VirtualDisplayAdapter failed to create DisplayDevice");
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index be03a80..f994c05 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -320,7 +320,9 @@
public void stop() {
mStopped = true;
mPhotonicModulator.interrupt();
- dismissColorFade();
+ if (mColorFade != null) {
+ mColorFade.destroy();
+ }
mCleanListener = null;
mHandler.removeCallbacksAndMessages(null);
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index b002587..90e32a6 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -64,7 +64,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
-import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A display adapter that provides virtual displays on behalf of applications.
@@ -72,15 +72,17 @@
* Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-@VisibleForTesting
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class VirtualDisplayAdapter extends DisplayAdapter {
static final String TAG = "VirtualDisplayAdapter";
- static final boolean DEBUG = false;
// Unique id prefix for virtual displays
@VisibleForTesting
static final String UNIQUE_ID_PREFIX = "virtual:";
+ // Unique id suffix for virtual displays
+ private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0);
+
private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();
private final Handler mHandler;
private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
@@ -111,8 +113,8 @@
}
public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
- IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface,
- int flags, VirtualDisplayConfig virtualDisplayConfig) {
+ IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId,
+ Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) {
IBinder appToken = callback.asBinder();
if (mVirtualDisplayDevices.containsKey(appToken)) {
Slog.wtfStack(TAG,
@@ -125,23 +127,13 @@
IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure,
virtualDisplayConfig.getRequestedRefreshRate());
- final String baseUniqueId =
- UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
- final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
- String uniqueId = virtualDisplayConfig.getUniqueId();
- if (uniqueId == null) {
- uniqueId = baseUniqueId + uniqueIndex;
- } else {
- uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
- }
MediaProjectionCallback mediaProjectionCallback = null;
if (projection != null) {
mediaProjectionCallback = new MediaProjectionCallback(appToken);
}
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
- ownerUid, ownerPackageName, surface, flags,
- new Callback(callback, mHandler), projection, mediaProjectionCallback,
- uniqueId, uniqueIndex, virtualDisplayConfig);
+ ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+ projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
@@ -219,26 +211,20 @@
}
/**
- * Returns the next unique index for the uniqueIdPrefix
+ * Generates a virtual display's unique identifier.
+ *
+ * <p>It is always prefixed with "virtual:package-name". If the provided config explicitly
+ * specifies a unique ID, then it's simply appended. Otherwise, the UID, display name and a
+ * unique index are appended.</p>
+ *
+ * <p>The unique index is incremented for every virtual display unique ID generation and serves
+ * for differentiating between displays with the same name created by the same owner.</p>
*/
- private int getNextUniqueIndex(String uniqueIdPrefix) {
- if (mVirtualDisplayDevices.isEmpty()) {
- return 0;
- }
-
- int nextUniqueIndex = 0;
- Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator();
- while (it.hasNext()) {
- VirtualDisplayDevice device = it.next();
- if (device.getUniqueId().startsWith(uniqueIdPrefix)
- && device.mUniqueIndex >= nextUniqueIndex) {
- // Increment the next unique index to be greater than ones we have already ran
- // across for displays that have the same unique Id prefix.
- nextUniqueIndex = device.mUniqueIndex + 1;
- }
- }
-
- return nextUniqueIndex;
+ static String generateDisplayUniqueId(String packageName, int uid,
+ VirtualDisplayConfig config) {
+ return UNIQUE_ID_PREFIX + packageName + ((config.getUniqueId() != null)
+ ? (":" + config.getUniqueId())
+ : ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement()));
}
private void handleBinderDiedLocked(IBinder appToken) {
@@ -278,7 +264,6 @@
private int mDisplayState;
private boolean mStopped;
private int mPendingChanges;
- private int mUniqueIndex;
private Display.Mode mMode;
private boolean mIsDisplayOn;
private int mDisplayIdToMirror;
@@ -287,7 +272,7 @@
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
Callback callback, IMediaProjection projection,
- IMediaProjectionCallback mediaProjectionCallback, String uniqueId, int uniqueIndex,
+ IMediaProjectionCallback mediaProjectionCallback, String uniqueId,
VirtualDisplayConfig virtualDisplayConfig) {
super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
mAppToken = appToken;
@@ -306,7 +291,6 @@
mMediaProjectionCallback = mediaProjectionCallback;
mDisplayState = Display.STATE_UNKNOWN;
mPendingChanges |= PENDING_SURFACE_CHANGE;
- mUniqueIndex = uniqueIndex;
mIsDisplayOn = surface != null;
mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 568618e..57e424d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -243,6 +243,10 @@
private static final String MIGRATED_FRP2 = "migrated_frp2";
private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
+ private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
+
+ private static final boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
+ android.security.Flags.fixUnlockedDeviceRequiredKeys();
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
@@ -856,9 +860,11 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
- // Notify keystore that a new user was added.
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- AndroidKeyStoreMaintenance.onUserAdded(userHandle);
+ if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ // Notify keystore that a new user was added.
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ AndroidKeyStoreMaintenance.onUserAdded(userHandle);
+ }
} else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mStorage.prefetchUser(userHandle);
@@ -1022,24 +1028,53 @@
}
mEarlyCreatedUsers = null; // no longer needed
- // Also do a one-time migration of all users to SP-based credentials with the CE key
- // encrypted by the SP. This is needed for the system user on the first boot of a
- // device, as the system user is special and never goes through the user creation flow
- // that other users do. It is also needed for existing users on a device upgraded from
- // Android 13 or earlier, where users with no LSKF didn't necessarily have an SP, and if
- // they did have an SP then their CE key wasn't encrypted by it.
+ // Do a one-time migration for any unsecured users: create the user's synthetic password
+ // if not already done, encrypt the user's CE key with the synthetic password if not
+ // already done, and create the user's Keystore super keys if not already done.
//
- // If this gets interrupted (e.g. by the device powering off), there shouldn't be a
- // problem since this will run again on the next boot, and setCeStorageProtection() is
- // okay with the CE key being already protected by the given secret.
- if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
- for (UserInfo user : mUserManager.getAliveUsers()) {
- removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
- synchronized (mSpManager) {
- migrateUserToSpWithBoundCeKeyLocked(user.id);
+ // This is needed for the following cases:
+ //
+ // - Finalizing the creation of the system user on the first boot of a device, as the
+ // system user is special and doesn't go through the normal user creation flow.
+ //
+ // - Upgrading from Android 13 or earlier, where unsecured users didn't necessarily have
+ // a synthetic password, and if they did have a synthetic password their CE key wasn't
+ // encrypted by it. Also, unsecured users didn't have Keystore super keys.
+ //
+ // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
+ //
+ // The end result is that all users, regardless of whether they are secured or not, have
+ // a synthetic password with all keys initialized and protected by it.
+ //
+ // Note: if this migration gets interrupted (e.g. by the device powering off), there
+ // shouldn't be a problem since this will run again on the next boot, and
+ // setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundKeysLocked(user.id);
+ }
}
+ setBoolean(MIGRATED_SP_FULL, true, 0);
}
- setString(MIGRATED_SP_CE_ONLY, "true", 0);
+ } else {
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundCeKeyLocked(user.id);
+ }
+ }
+ setString(MIGRATED_SP_CE_ONLY, "true", 0);
+ }
+
+ if (getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ // The FIX_UNLOCKED_DEVICE_REQUIRED_KEYS flag was enabled but then got disabled.
+ // Ensure the full migration runs again the next time the flag is enabled...
+ setBoolean(MIGRATED_SP_FULL, false, 0);
+ }
}
mThirdPartyAppsStarted = true;
@@ -1070,6 +1105,37 @@
}
}
+ @GuardedBy("mSpManager")
+ private void migrateUserToSpWithBoundKeysLocked(@UserIdInt int userId) {
+ if (isUserSecure(userId)) {
+ Slogf.d(TAG, "User %d is secured; no migration needed", userId);
+ return;
+ }
+ long protectorId = getCurrentLskfBasedProtectorId(userId);
+ if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
+ Slogf.i(TAG, "Migrating unsecured user %d to SP-based credential", userId);
+ initializeSyntheticPassword(userId);
+ return;
+ }
+ Slogf.i(TAG, "Existing unsecured user %d has a synthetic password", userId);
+ AuthenticationResult result = mSpManager.unlockLskfBasedProtector(
+ getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
+ null);
+ SyntheticPassword sp = result.syntheticPassword;
+ if (sp == null) {
+ Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
+ return;
+ }
+ // While setCeStorageProtection() is idempotent, it does log some error messages when called
+ // again. Skip it if we know it was already handled by an earlier upgrade to Android 14.
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+ Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
+ setCeStorageProtection(userId, sp);
+ }
+ Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId);
+ initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
+ }
+
/**
* Returns the lowest password quality that still presents the same UI for entering it.
*
@@ -1351,6 +1417,20 @@
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
}
+ @VisibleForTesting /** Note: this method is overridden in unit tests */
+ void initKeystoreSuperKeys(@UserIdInt int userId, SyntheticPassword sp, boolean allowExisting) {
+ final byte[] password = sp.deriveKeyStorePassword();
+ try {
+ int res = AndroidKeyStoreMaintenance.initUserSuperKeys(userId, password, allowExisting);
+ if (res != 0) {
+ throw new IllegalStateException("Failed to initialize Keystore super keys for user "
+ + userId);
+ }
+ } finally {
+ Arrays.fill(password, (byte) 0);
+ }
+ }
+
private void unlockKeystore(int userId, SyntheticPassword sp) {
Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
}
@@ -2074,6 +2154,9 @@
return;
}
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ unlockKeystore(userId, result.syntheticPassword);
+ }
unlockCeStorage(userId, result.syntheticPassword);
}
}
@@ -2353,6 +2436,16 @@
}
private void createNewUser(@UserIdInt int userId, int userSerialNumber) {
+
+ // Delete all Keystore keys for userId, just in case any were left around from a removed
+ // user with the same userId. This should be unnecessary, but we've been doing this for a
+ // long time, so for now we keep doing it just in case it's ever important. Don't wait
+ // until initKeystoreSuperKeys() to do this; that can be delayed if the user is being
+ // created during early boot, and maybe something will use Keystore before then.
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ AndroidKeyStoreMaintenance.onUserAdded(userId);
+ }
+
synchronized (mUserCreationAndRemovalLock) {
// During early boot, don't actually create the synthetic password yet, but rather
// automatically delay it to later. We do this because protecting the synthetic
@@ -2759,7 +2852,7 @@
/**
* Creates the synthetic password (SP) for the given user, protects it with an empty LSKF, and
- * protects the user's CE key with a key derived from the SP.
+ * protects the user's CE storage key and Keystore super keys with keys derived from the SP.
*
* <p>This is called just once in the lifetime of the user: at user creation time (possibly
* delayed until the time when Weaver is guaranteed to be available), or when upgrading from
@@ -2778,6 +2871,9 @@
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
setCeStorageProtection(userId, sp);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
+ }
onSyntheticPasswordCreated(userId, sp);
Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
@@ -2870,11 +2966,10 @@
/**
* Changes the user's LSKF by creating an LSKF-based protector that uses the new LSKF (which may
* be empty) and replacing the old LSKF-based protector with it. The SP itself is not changed.
- *
- * Also maintains the invariants described in {@link SyntheticPasswordManager} by
- * setting/clearing the protection (by the SP) on the user's auth-bound Keystore keys when the
- * LSKF is added/removed, respectively. If an LSKF is being added, then the Gatekeeper auth
- * token is also refreshed.
+ * <p>
+ * Also maintains the invariants described in {@link SyntheticPasswordManager} by enrolling /
+ * deleting the synthetic password into Gatekeeper as the LSKF is set / cleared, and asking
+ * Keystore to delete the user's auth-bound keys when the LSKF is cleared.
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
@@ -2893,7 +2988,9 @@
if (!mSpManager.hasSidForUser(userId)) {
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
- setKeystorePassword(sp.deriveKeyStorePassword(), userId);
+ if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ setKeystorePassword(sp.deriveKeyStorePassword(), userId);
+ }
}
} else {
// Cache all profile password if they use unified work challenge. This will later be
@@ -2904,7 +3001,11 @@
gateKeeperClearSecureUserId(userId);
unlockCeStorage(userId, sp);
unlockKeystore(userId, sp);
- setKeystorePassword(null, userId);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
+ } else {
+ setKeystorePassword(null, userId);
+ }
removeBiometricsForUser(userId);
}
setCurrentLskfBasedProtectorId(newProtectorId, userId);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 8e9c21f..cc205d4 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -90,10 +90,15 @@
*
* - The user's credential-encrypted storage is always protected by the SP.
*
- * - The user's auth-bound Keystore keys are protected by the SP, but only while an LSKF is set.
- * This works by setting the user's Keystore and Gatekeeper passwords to SP-derived secrets, but
- * only while an LSKF is set. When the LSKF is removed, these passwords are cleared,
- * invalidating the user's auth-bound keys.
+ * - The user's Keystore superencryption keys are always protected by the SP. These in turn
+ * protect the Keystore keys that require user authentication, an unlocked device, or both.
+ *
+ * - A secret derived from the synthetic password is enrolled in Gatekeeper for the user, but only
+ * while the user has a (nonempty) LSKF. This enrollment has an associated ID called the Secure
+ * user ID or SID. This use of Gatekeeper, which is separate from the use of GateKeeper that may
+ * be used in the LSKF-based protector, makes it so that unlocking the synthetic password
+ * generates a HardwareAuthToken (but only when the user has LSKF). That HardwareAuthToken can
+ * be provided to KeyMint to authorize the use of the user's authentication-bound Keystore keys.
*
* Files stored on disk for each user:
* For the SP itself, stored under NULL_PROTECTOR_ID:
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index 392b86e..681d1a0 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -327,10 +327,10 @@
/**
* Notify our observers of a change in the data activity state of the interface
*/
- private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
+ private void notifyInterfaceClassActivity(int label, boolean isActive, long tsNanos,
int uid) {
invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
- type, isActive, tsNanos, uid));
+ label, isActive, tsNanos, uid));
}
// Sync the state of the given chain with the native daemon.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4b466be..52fdfd1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1616,6 +1616,13 @@
}
}
+ for (Checksum checksum : checksums) {
+ if (checksum.getValue() == null
+ || checksum.getValue().length > Checksum.MAX_CHECKSUM_SIZE_BYTES) {
+ throw new IllegalArgumentException("Invalid checksum.");
+ }
+ }
+
assertCallerIsOwnerOrRoot();
synchronized (mLock) {
assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7d3d85d..ec3823f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3105,7 +3105,8 @@
}
private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot,
- String callingPackage, int callingUid, int userId, String callingMethod) {
+ boolean quarantined, String callingPackage, int callingUid, int userId,
+ String callingMethod) {
if (callingUid == Process.ROOT_UID
// Need to compare app-id to allow system dialogs access on secondary users
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
@@ -3120,8 +3121,20 @@
}
}
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
- callingMethod);
+ if (quarantined) {
+ final boolean hasQuarantineAppsPerm = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.QUARANTINE_APPS) == PERMISSION_GRANTED;
+ // TODO: b/305256093 - In order to facilitate testing, temporarily allowing apps
+ // with SUSPEND_APPS permission to quarantine apps. Remove this once the testing
+ // is done and this is no longer needed.
+ if (!hasQuarantineAppsPerm) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ callingMethod);
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ callingMethod);
+ }
final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId);
final boolean allowedPackageUid = packageUid == callingUid;
@@ -6136,9 +6149,6 @@
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) {
final int callingUid = Binder.getCallingUid();
- final Computer snapshot = snapshotComputer();
- enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId,
- "setPackagesSuspendedAsUser");
boolean quarantined = false;
if (Flags.quarantinedEnabled()) {
if ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) {
@@ -6149,6 +6159,9 @@
quarantined = callingPackage.equals(wellbeingPkg);
}
}
+ final Computer snapshot = snapshotComputer();
+ enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid,
+ userId, "setPackagesSuspendedAsUser");
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
quarantined);
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 99064bc..d17207b 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -28,6 +28,7 @@
import android.hardware.thermal.V1_1.IThermalCallback;
import android.os.Binder;
import android.os.CoolingDevice;
+import android.os.Flags;
import android.os.Handler;
import android.os.HwBinder;
import android.os.IBinder;
@@ -181,7 +182,7 @@
onTemperatureChanged(temperatures.get(i), false);
}
onTemperatureMapChangedLocked();
- mTemperatureWatcher.updateSevereThresholds();
+ mTemperatureWatcher.updateThresholds();
mHalReady.set(true);
}
}
@@ -506,6 +507,20 @@
}
@Override
+ public float[] getThermalHeadroomThresholds() {
+ if (!mHalReady.get()) {
+ throw new IllegalStateException("Thermal HAL connection is not initialized");
+ }
+ if (!Flags.allowThermalHeadroomThresholds()) {
+ throw new UnsupportedOperationException("Thermal headroom thresholds not enabled");
+ }
+ synchronized (mTemperatureWatcher.mSamples) {
+ return Arrays.copyOf(mTemperatureWatcher.mHeadroomThresholds,
+ mTemperatureWatcher.mHeadroomThresholds.length);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
dumpInternal(fd, pw, args);
}
@@ -580,6 +595,12 @@
mHalWrapper.getTemperatureThresholds(false, 0));
}
}
+ if (Flags.allowThermalHeadroomThresholds()) {
+ synchronized (mTemperatureWatcher.mSamples) {
+ pw.println("Temperature headroom thresholds:");
+ pw.println(Arrays.toString(mTemperatureWatcher.mHeadroomThresholds));
+ }
+ }
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -964,7 +985,14 @@
connectToHal();
}
if (mInstance != null) {
- Slog.i(TAG, "Thermal HAL AIDL service connected.");
+ try {
+ Slog.i(TAG, "Thermal HAL AIDL service connected with version "
+ + mInstance.getInterfaceVersion());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to read interface version from Thermal HAL", e);
+ connectToHal();
+ return;
+ }
registerThermalChangedCallback();
}
}
@@ -1440,26 +1468,55 @@
ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>();
@GuardedBy("mSamples")
+ float[] mHeadroomThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
+ @GuardedBy("mSamples")
private long mLastForecastCallTimeMillis = 0;
private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
@VisibleForTesting
long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;
- void updateSevereThresholds() {
+ void updateThresholds() {
synchronized (mSamples) {
List<TemperatureThreshold> thresholds =
mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
+ if (Flags.allowThermalHeadroomThresholds()) {
+ Arrays.fill(mHeadroomThresholds, Float.NaN);
+ }
for (int t = 0; t < thresholds.size(); ++t) {
TemperatureThreshold threshold = thresholds.get(t);
if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) {
continue;
}
- float temperature =
+ float severeThreshold =
threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE];
- if (!Float.isNaN(temperature)) {
- mSevereThresholds.put(threshold.name,
- threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]);
+ if (!Float.isNaN(severeThreshold)) {
+ mSevereThresholds.put(threshold.name, severeThreshold);
+ for (int severity = ThrottlingSeverity.LIGHT;
+ severity <= ThrottlingSeverity.SHUTDOWN; severity++) {
+ if (Flags.allowThermalHeadroomThresholds()
+ && threshold.hotThrottlingThresholds.length > severity) {
+ updateHeadroomThreshold(severity,
+ threshold.hotThrottlingThresholds[severity],
+ severeThreshold);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // For a older device with multiple SKIN sensors, we will set a severity's headroom
+ // threshold based on the minimum value of all as a workaround.
+ void updateHeadroomThreshold(int severity, float threshold, float severeThreshold) {
+ if (!Float.isNaN(threshold)) {
+ synchronized (mSamples) {
+ float headroom = normalizeTemperature(threshold, severeThreshold);
+ if (Float.isNaN(mHeadroomThresholds[severity])) {
+ mHeadroomThresholds[severity] = headroom;
+ } else {
+ float lastHeadroom = mHeadroomThresholds[severity];
+ mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom);
}
}
}
@@ -1541,15 +1598,13 @@
private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f;
@VisibleForTesting
- float normalizeTemperature(float temperature, float severeThreshold) {
- synchronized (mSamples) {
- float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
- if (temperature <= zeroNormalized) {
- return 0.0f;
- }
- float delta = temperature - zeroNormalized;
- return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
+ static float normalizeTemperature(float temperature, float severeThreshold) {
+ float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
+ if (temperature <= zeroNormalized) {
+ return 0.0f;
}
+ float delta = temperature - zeroNormalized;
+ return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
}
private static final int MINIMUM_SAMPLE_COUNT = 3;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
index 3f02266..f48178c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperDisplayHelper.java
@@ -124,7 +124,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- return mWindowManagerInternal.shouldShowSystemDecorOnDisplay(displayId);
+ return mWindowManagerInternal.isHomeSupportedOnDisplay(displayId);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3c56a4e..3ec58f0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -414,6 +414,8 @@
boolean mHasCompanionDeviceSetupFeature;
/** The process of the top most activity. */
volatile WindowProcessController mTopApp;
+ /** The process showing UI while the device is dozing. */
+ volatile WindowProcessController mVisibleDozeUiProcess;
/**
* This is the process holding the activity the user last visited that is in a different process
* from the one they are currently in.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7f80807..4924810 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -184,6 +184,7 @@
import android.graphics.Region.Op;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.VirtualDisplayConfig;
import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Debug;
@@ -2191,7 +2192,7 @@
* @see DisplayWindowPolicyController#getCustomHomeComponent() ()
*/
@Nullable ComponentName getCustomHomeComponent() {
- if (!supportsSystemDecorations() || mDwpcHelper == null) {
+ if (!isHomeSupported() || mDwpcHelper == null) {
return null;
}
return mDwpcHelper.getCustomHomeComponent();
@@ -5772,6 +5773,17 @@
&& isTrusted();
}
+ /**
+ * Checks if this display is configured and allowed to show home activity and wallpaper.
+ *
+ * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}
+ * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p>
+ */
+ boolean isHomeSupported() {
+ return (mWmService.mDisplayWindowSettings.isHomeSupportedLocked(this) && isTrusted())
+ || supportsSystemDecorations();
+ }
+
SurfaceControl getWindowingLayer() {
return mWindowingLayer;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index b34f912..708ee7f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -99,6 +99,7 @@
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.PrintWriterPrinter;
@@ -781,6 +782,12 @@
if (!mDisplayContent.isDefaultDisplay) {
return;
}
+ if (awake) {
+ mService.mAtmService.mVisibleDozeUiProcess = null;
+ } else if (mScreenOnFully && mNotificationShade != null) {
+ // Screen is still on, so it may be showing an always-on UI.
+ mService.mAtmService.mVisibleDozeUiProcess = mNotificationShade.getProcess();
+ }
mService.mAtmService.mKeyguardController.updateDeferTransitionForAod(
mAwake /* waiting */);
}
@@ -826,12 +833,24 @@
}
public void screenTurnedOn(ScreenOnListener screenOnListener) {
+ WindowProcessController visibleDozeUiProcess = null;
synchronized (mLock) {
mScreenOnEarly = true;
mScreenOnFully = false;
mKeyguardDrawComplete = false;
mWindowManagerDrawComplete = false;
mScreenOnListener = screenOnListener;
+ if (!mAwake && mNotificationShade != null) {
+ // The screen is turned on without awake state. It is usually triggered by an
+ // adding notification, so make the UI process have a higher priority.
+ visibleDozeUiProcess = mNotificationShade.getProcess();
+ mService.mAtmService.mVisibleDozeUiProcess = visibleDozeUiProcess;
+ }
+ }
+ // The method calls AM directly, so invoke it outside the lock.
+ if (visibleDozeUiProcess != null) {
+ Trace.instant(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurnedOnWhileDozing");
+ mService.mAtmService.setProcessAnimatingWhileDozing(visibleDozeUiProcess);
}
}
@@ -842,6 +861,7 @@
mKeyguardDrawComplete = false;
mWindowManagerDrawComplete = false;
mScreenOnListener = null;
+ mService.mAtmService.mVisibleDozeUiProcess = null;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index e1753d7..7a95c2d 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -237,6 +237,37 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ boolean isHomeSupportedLocked(@NonNull DisplayContent dc) {
+ if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ // Default display should show home.
+ return true;
+ }
+
+ final DisplayInfo displayInfo = dc.getDisplayInfo();
+ final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
+ return settings.mIsHomeSupported != null
+ ? settings.mIsHomeSupported
+ : shouldShowSystemDecorsLocked(dc);
+ }
+
+ void setHomeSupportedOnDisplayLocked(@NonNull String displayUniqueId, int displayType,
+ boolean supported) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = displayUniqueId;
+ displayInfo.type = displayType;
+ final SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getOverrideSettings(displayInfo);
+ overrideSettings.mIsHomeSupported = supported;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
+ void clearDisplaySettings(@NonNull String displayUniqueId, int displayType) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.uniqueId = displayUniqueId;
+ displayInfo.type = displayType;
+ mSettingsProvider.clearDisplaySettings(displayInfo);
+ }
+
@DisplayImePolicy
int getImePolicyLocked(@NonNull DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
@@ -382,11 +413,18 @@
void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides);
/**
- * Called when a display is removed to cleanup.
+ * Called when a display is removed to cleanup. Note that for non-virtual displays the
+ * relevant settings entry will be kept, if non-empty.
*/
void onDisplayRemoved(@NonNull DisplayInfo info);
/**
+ * Explicitly removes all settings entory for the given {@link DisplayInfo}, even if it is
+ * not empty.
+ */
+ void clearDisplaySettings(@NonNull DisplayInfo info);
+
+ /**
* Settings for a display.
*/
class SettingsEntry {
@@ -411,6 +449,8 @@
@Nullable
Boolean mShouldShowSystemDecors;
@Nullable
+ Boolean mIsHomeSupported;
+ @Nullable
Integer mImePolicy;
@Nullable
Integer mFixedToUserRotation;
@@ -479,6 +519,10 @@
mShouldShowSystemDecors = other.mShouldShowSystemDecors;
changed = true;
}
+ if (!Objects.equals(other.mIsHomeSupported, mIsHomeSupported)) {
+ mIsHomeSupported = other.mIsHomeSupported;
+ changed = true;
+ }
if (!Objects.equals(other.mImePolicy, mImePolicy)) {
mImePolicy = other.mImePolicy;
changed = true;
@@ -561,6 +605,11 @@
mShouldShowSystemDecors = delta.mShouldShowSystemDecors;
changed = true;
}
+ if (delta.mIsHomeSupported != null && !Objects.equals(
+ delta.mIsHomeSupported, mIsHomeSupported)) {
+ mIsHomeSupported = delta.mIsHomeSupported;
+ changed = true;
+ }
if (delta.mImePolicy != null
&& !Objects.equals(delta.mImePolicy, mImePolicy)) {
mImePolicy = delta.mImePolicy;
@@ -599,6 +648,7 @@
&& mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
&& mShouldShowWithInsecureKeyguard == null
&& mShouldShowSystemDecors == null
+ && mIsHomeSupported == null
&& mImePolicy == null
&& mFixedToUserRotation == null
&& mIgnoreOrientationRequest == null
@@ -622,6 +672,7 @@
&& Objects.equals(mShouldShowWithInsecureKeyguard,
that.mShouldShowWithInsecureKeyguard)
&& Objects.equals(mShouldShowSystemDecors, that.mShouldShowSystemDecors)
+ && Objects.equals(mIsHomeSupported, that.mIsHomeSupported)
&& Objects.equals(mImePolicy, that.mImePolicy)
&& Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
&& Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
@@ -633,9 +684,9 @@
public int hashCode() {
return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
- mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
- mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
- mDontMoveToTop);
+ mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mIsHomeSupported,
+ mImePolicy, mFixedToUserRotation, mIgnoreOrientationRequest,
+ mIgnoreDisplayCutout, mDontMoveToTop);
}
@Override
@@ -651,6 +702,7 @@
+ ", mRemoveContentMode=" + mRemoveContentMode
+ ", mShouldShowWithInsecureKeyguard=" + mShouldShowWithInsecureKeyguard
+ ", mShouldShowSystemDecors=" + mShouldShowSystemDecors
+ + ", mIsHomeSupported=" + mIsHomeSupported
+ ", mShouldShowIme=" + mImePolicy
+ ", mFixedToUserRotation=" + mFixedToUserRotation
+ ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index ea668fa..c79565a 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -164,6 +164,11 @@
mOverrideSettings.onDisplayRemoved(info);
}
+ @Override
+ public void clearDisplaySettings(@NonNull DisplayInfo info) {
+ mOverrideSettings.clearDisplaySettings(info);
+ }
+
@VisibleForTesting
int getOverrideSettingsSize() {
return mOverrideSettings.mSettings.size();
@@ -291,6 +296,12 @@
}
}
+ void clearDisplaySettings(@NonNull DisplayInfo info) {
+ final String identifier = getIdentifier(info);
+ mSettings.remove(identifier);
+ mVirtualDisplayIdentifiers.remove(identifier);
+ }
+
private void writeSettings() {
final FileData fileData = new FileData();
fileData.mIdentifierType = mIdentifierType;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c8fd16d..997b608 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -393,8 +393,12 @@
*/
void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) {
final boolean clear = activity == null;
+ final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null;
mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer);
+ if (clear && wasActive) {
+ setUpdateInputWindowsNeededLw();
+ }
}
private static <T> T getWeak(WeakReference<T> ref) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5227a52..fe2c250 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1696,8 +1696,8 @@
}
final DisplayContent display = taskDisplayArea.getDisplayContent();
- if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
- // Can't launch home on display that doesn't support system decorations.
+ if (display == null || display.isRemoved() || !display.isHomeSupported()) {
+ // Can't launch home on display that doesn't support home.
return false;
}
@@ -3126,10 +3126,11 @@
if (preferredFocusableRootTask != null) {
return preferredFocusableRootTask;
}
- if (preferredDisplayArea.mDisplayContent.supportsSystemDecorations()) {
+
+ if (preferredDisplayArea.mDisplayContent.isHomeSupported()) {
// Stop looking for focusable root task on other displays because the preferred display
- // supports system decorations. Home activity would be launched on the same display if
- // no focusable root task found.
+ // supports home. Home activity would be launched on the same display if no focusable
+ // root task found.
return null;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c9703db..267d148 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3513,7 +3513,8 @@
}
appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton = top != null
&& !appCompatTaskInfo.topActivityInSizeCompat
- && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings();
+ && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings()
+ && !info.isTopActivityTransparent;
appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index f0a6654..c57983c 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1767,7 +1767,7 @@
* Exposes the home task capability of the TaskDisplayArea
*/
boolean canHostHomeTask() {
- return mDisplayContent.supportsSystemDecorations() && mCanHostHomeTask;
+ return mDisplayContent.isHomeSupported() && mCanHostHomeTask;
}
/**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f700944..caa57bb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1344,6 +1344,7 @@
final DisplayContent dc =
mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
+ dc.getInputMonitor().updateInputWindowsLw(false /* force */);
}
if (mTransientLaunches != null) {
for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9f1bccb..92bd00e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -28,6 +28,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.VirtualDisplayConfig;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
@@ -752,9 +753,31 @@
public abstract Context getTopFocusedDisplayUiContext();
/**
- * Checks if this display is configured and allowed to show system decorations.
+ * Sets whether the relevant display content can host the relevant home activity and wallpaper.
+ *
+ * @param displayUniqueId The unique ID of the display. Note that the display may not yet be
+ * created, but whenever it is, this property will be applied.
+ * @param displayType The type of the display, e.g. {@link Display#TYPE_VIRTUAL}.
+ * @param supported Whether home and wallpaper are supported on this display.
*/
- public abstract boolean shouldShowSystemDecorOnDisplay(int displayId);
+ public abstract void setHomeSupportedOnDisplay(
+ @NonNull String displayUniqueId, int displayType, boolean supported);
+
+ /**
+ * Checks if this display is configured and allowed to show home activity and wallpaper.
+ *
+ * <p>This is implied for displays that have {@link Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}
+ * and can also be set via {@link VirtualDisplayConfig.Builder#setHomeSupported}.</p>
+ */
+ public abstract boolean isHomeSupportedOnDisplay(int displayId);
+
+ /**
+ * Removes any settings relevant to the given display.
+ *
+ * <p>This may be used when a property is set for a display unique ID before the display
+ * creation but the actual display creation failed for some reason.</p>
+ */
+ public abstract void clearDisplaySettings(@NonNull String displayUniqueId, int displayType);
/**
* Indicates the policy for how the display should show IME.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 757d6d6..809e2d0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8269,9 +8269,41 @@
}
@Override
- public boolean shouldShowSystemDecorOnDisplay(int displayId) {
+ public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType,
+ boolean supported) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mDisplayWindowSettings.setHomeSupportedOnDisplayLocked(
+ displayUniqueId, displayType, supported);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean isHomeSupportedOnDisplay(int displayId) {
synchronized (mGlobalLock) {
- return WindowManagerService.this.shouldShowSystemDecors(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ ProtoLog.w(WM_ERROR, "Attempted to get home support flag of a display that "
+ + "does not exist: %d", displayId);
+ return false;
+ }
+ return displayContent.isHomeSupported();
+ }
+ }
+
+ @Override
+ public void clearDisplaySettings(String displayUniqueId, int displayType) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mDisplayWindowSettings.clearDisplaySettings(displayUniqueId, displayType);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index a74a707..e9d8038 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1864,6 +1864,11 @@
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
+ public boolean isShowingUiWhileDozing() {
+ return this == mAtm.mVisibleDozeUiProcess;
+ }
+
+ @HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean isPreviousProcess() {
return this == mAtm.mPreviousProcess;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 2182093..f1cddc6 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -2557,11 +2557,10 @@
static void nativeSetKeyRepeatConfiguration(JNIEnv* env, jobject nativeImplObj, jint timeoutMs,
jint delayMs) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(static_cast<nsecs_t>(
- timeoutMs) *
- 1000000,
- static_cast<nsecs_t>(delayMs) *
- 1000000);
+ im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(std::chrono::milliseconds(
+ timeoutMs),
+ std::chrono::milliseconds(
+ delayMs));
}
static jobject createInputSensorInfo(JNIEnv* env, jstring name, jstring vendor, jint version,
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index 6e4069f..3bafe72 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -7,19 +7,12 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-// Include all test java files.
-filegroup {
- name: "displayservicetests-sources",
- srcs: [
- "src/**/*.java",
- ],
-}
-
android_test {
name: "DisplayServiceTests",
srcs: [
"src/**/*.java",
+ "src/**/*.kt",
],
libs: [
@@ -33,6 +26,8 @@
"frameworks-base-testutils",
"junit",
"junit-params",
+ "kotlin-test",
+ "mockito-kotlin2",
"mockingservicestests-utils-mockito",
"platform-compat-test-rules",
"platform-test-annotations",
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3775ac94..0bf4654 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -196,7 +196,8 @@
@Rule(order = 1)
public Expect expect = Expect.create();
@Rule
- public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public SetFlagsRule mSetFlagsRule =
+ new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
private Context mContext;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
new file mode 100644
index 0000000..49fa254
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display
+
+import android.view.Display
+import androidx.test.filters.SmallTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.junit.MockitoJUnit
+
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@SmallTest
+class DisplayPowerStateTest {
+
+ private lateinit var displayPowerState: DisplayPowerState
+
+ @get:Rule
+ val mockitoRule = MockitoJUnit.rule()
+
+ private val mockBlanker = mock<DisplayBlanker>()
+ private val mockColorFade = mock<ColorFade>()
+
+ @Before
+ fun setUp() {
+ displayPowerState = DisplayPowerState(mockBlanker, mockColorFade, 123, Display.STATE_ON)
+ }
+
+ @Test
+ fun `destroys ColorFade on stop`() {
+ displayPowerState.stop()
+
+ verify(mockColorFade).destroy()
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index 8bbacc4..73e7ba0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -77,7 +77,7 @@
DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
/* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
- /* surface= */ null, /* flags= */ 0, config);
+ /* uniqueId= */ "uniqueId", /* surface= */ null, /* flags= */ 0, config);
assertNotNull(result);
}
@@ -89,12 +89,12 @@
VirtualDisplayConfig config2 = new VirtualDisplayConfig.Builder("test2", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback, /* projection= */ null,
- /* ownerUid= */ 10, /* packageName= */ "testpackage", /* surface= */ null,
- /* flags= */ 0, config1);
+ /* ownerUid= */ 10, /* packageName= */ "testpackage", /* uniqueId= */ "uniqueId1",
+ /* surface= */ null, /* flags= */ 0, config1);
DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(mMockCallback,
/* projection= */ null, /* ownerUid= */ 10, /* packageName= */ "testpackage",
- /* surface= */ null, /* flags= */ 0, config2);
+ /* uniqueId= */ "uniqueId2", /* surface= */ null, /* flags= */ 0, config2);
assertNull(result);
}
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 063af57..113511e 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -46,6 +46,7 @@
"androidx.test.espresso.core",
"androidx.test.espresso.contrib",
"androidx.test.ext.truth",
+ "backup_flags_lib",
"flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
new file mode 100644
index 0000000..0006d0a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.restore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.RestoreSet;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+import com.android.server.LocalServices;
+import com.android.server.backup.Flags;
+import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.utils.BackupEligibilityRules;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActiveRestoreSessionTest {
+ private static final String TEST_APP_NAME = "test_app";
+
+ private ActiveRestoreSession mRestoreSession;
+ private ApplicationInfo mTestApp;
+
+ @Mock
+ private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private BackupEligibilityRules mBackupEligibilityRules;
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private TransportManager mTransportManager;
+ @Mock private UserManager mUserManager;
+
+ @Rule
+ public final SetFlagsRule mFlagsRule = new SetFlagsRule();
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule
+ .Builder(/* testClassInstance */ this)
+ .mockStatic(LocalServices.class)
+ .afterSessionFinished(
+ () -> LocalServices.removeServiceForTest(PackageManagerInternal.class)
+ ).build();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(/* testClass */ this);
+ when(mBackupEligibilityRules.isAppEligibleForRestore(any())).thenReturn(true);
+ when(mBackupManagerService.getEligibilityRulesForOperation(anyInt())).thenReturn(
+ mBackupEligibilityRules);
+ when(mBackupManagerService.getTransportManager()).thenReturn(mTransportManager);
+ when(mBackupManagerService.getContext()).thenReturn(mContext);
+ when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
+ when(LocalServices.getService(PackageManagerInternal.class)).thenReturn(
+ mPackageManagerInternal);
+
+ mRestoreSession = new ActiveRestoreSession(mBackupManagerService,
+ /* packageName */ null,
+ /* transportName */ "",
+ mBackupEligibilityRules);
+ mTestApp = new ApplicationInfo();
+ mTestApp.packageName = TEST_APP_NAME;
+ }
+
+ @Test
+ public void testGetBackupEligibilityRules_skipRestoreFlagOn_skipsLaunchedAppRestore() {
+ mFlagsRule.enableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS);
+ RestoreSet restoreSet = new RestoreSet(
+ /* name */ null,
+ /* device */ null,
+ /* token */ 0,
+ /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS);
+ when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt()))
+ .thenReturn(true);
+
+ BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules(
+ restoreSet);
+
+ assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isFalse();
+ }
+
+ @Test
+ public void testGetBackupEligibilityRules_skipRestoreFlagOff_allowsAppRestore() {
+ mFlagsRule.disableFlags(Flags.FLAG_ENABLE_SKIPPING_RESTORE_LAUNCHED_APPS);
+ RestoreSet restoreSet = new RestoreSet(
+ /* name */ null,
+ /* device */ null,
+ /* token */ 0,
+ /* backupTransportFlags */ BackupAgent.FLAG_SKIP_RESTORE_FOR_LAUNCHED_APPS);
+ when(mPackageManagerInternal.wasPackageEverLaunched(eq(TEST_APP_NAME), anyInt()))
+ .thenReturn(true);
+
+ BackupEligibilityRules eligibilityRules = mRestoreSession.getBackupEligibilityRules(
+ restoreSet);
+
+ assertThat(eligibilityRules.isAppEligibleForRestore(mTestApp)).isTrue();
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 0306655..290b260 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -860,6 +860,66 @@
assertThat(result).isFalse();
}
+ @Test
+ public void isAppEligibleForRestore_hasBeenLaunched_returnsFalse() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(true);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ null);
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ true);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isFalse();
+ }
+
+ @Test
+ public void isAppEligibleForRestore_hasNotBeenLaunched_returnsTrue() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(false);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ null);
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ false);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void isAppEligibleForRestore_launchedButHasBackupAgent_returnsTrue() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(true);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ "BackupAgent");
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ false);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
+ @Test
+ public void isAppEligibleForRestore_doNotSkipRestoreForLaunched_returnsTrue() {
+ when(mMockPackageManagerInternal.wasPackageEverLaunched(eq(TEST_PACKAGE_NAME), eq(mUserId)))
+ .thenReturn(true);
+ ApplicationInfo applicationInfo = getApplicationInfo(/* appUid */ 0, /* flags */ 0,
+ /* backupAgentName */ null);
+ BackupEligibilityRules backupEligibilityRules = new BackupEligibilityRules(mPackageManager,
+ mMockPackageManagerInternal, mUserId, mContext, BackupDestination.CLOUD,
+ /* skipRestoreForLaunchedApps */ false);
+
+ boolean isEligible = backupEligibilityRules.isAppEligibleForRestore(applicationInfo);
+
+ assertThat(isEligible).isTrue();
+ }
+
private BackupEligibilityRules getBackupEligibilityRules(
@BackupDestination int backupDestination) {
return new BackupEligibilityRules(mPackageManager, mMockPackageManagerInternal, mUserId,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 1c33d0d..18961c0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -34,6 +34,7 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
+import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPassword;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.pm.UserManagerInternal;
@@ -203,6 +204,10 @@
}
@Override
+ void initKeystoreSuperKeys(int userId, SyntheticPassword sp, boolean allowExisting) {
+ }
+
+ @Override
protected boolean isCredentialSharableWithParent(int userId) {
UserInfo userInfo = mUserManager.getUserInfo(userId);
return userInfo.isCloneProfile() || userInfo.isManagedProfile();
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
index cf2b748..ee23a00 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt
@@ -492,6 +492,40 @@
validateTagAttr(tag, "name", R.styleable.AndroidManifestUsesPermission_name, 1024)
}
+ @Test
+ fun totalMetadataValuesExceedMax_shouldFail() {
+ val value = "x".repeat(8192)
+ var tags = ""
+ repeat(32) { index ->
+ tags += "<meta-data name=\"name$index\" value=\"$value\" />"
+ }
+ var xml = "<application>$tags</application>"
+ try {
+ parseXmlForMetadata(xml)
+ } catch (e: SecurityException) {
+ fail(
+ "Failed to parse component meta-data when values have not exceeded max allowed"
+ )
+ }
+ try {
+ parseXmlForMetadataRes(xml)
+ } catch (e: SecurityException) {
+ fail(
+ "Failed to parse component meta-data when values have not exceeded max allowed"
+ )
+ }
+ tags += "<meta-data name=\"last\" value=\"x\" />"
+ xml = "<application>$tags</application>"
+ var e = assertThrows(SecurityException::class.java) {
+ parseXmlForMetadata(xml)
+ }
+ assertEquals("Max total meta data size limit exceeded for application", e.message)
+ e = assertThrows(SecurityException::class.java) {
+ parseXmlForMetadataRes(xml)
+ }
+ assertEquals("Max total meta data size limit exceeded for application", e.message)
+ }
+
private fun validateTagAttrComponentName(tag: String, attr: String, index: Int) {
val passNames = arrayOf("com.android.TestClass", "TestClass", "_", "$", ".TestClass", "上")
for (name in passNames) {
@@ -664,6 +698,38 @@
} while (type != XmlPullParser.END_DOCUMENT)
}
+ fun parseXmlForMetadata(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ if (type == XmlPullParser.START_TAG && pullParser.getName().equals("meta-data")) {
+ val name = "value"
+ val value = pullParser.getAttributeValue("", name)
+ validator.validateStrAttr(pullParser, "value", value)
+ }
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
+ fun parseXmlForMetadataRes(manifestStr: String) {
+ pullParser.setInput(ByteArrayInputStream(manifestStr.toByteArray()), null)
+ val validator = Validator()
+ do {
+ val type = pullParser.next()
+ validator.validate(pullParser)
+ if (type == XmlPullParser.START_TAG && pullParser.getName().equals("meta-data")) {
+ val name = "value"
+ val value = pullParser.getAttributeValue("", name)
+ validator.validateResStrAttr(
+ pullParser,
+ R.styleable.AndroidManifestMetaData_value,
+ value
+ )
+ }
+ } while (type != XmlPullParser.END_DOCUMENT)
+ }
+
fun expectedCountErrorMsg(tag: String, parentTag: String) =
"The number of child $tag elements exceeded the max allowed in $parentTag"
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 13c011a..44dad59 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -18,9 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -28,6 +30,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -49,6 +52,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.SystemService;
+import com.android.server.power.ThermalManagerService.TemperatureWatcher;
import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
import org.junit.Before;
@@ -65,6 +69,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
/**
* atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
@@ -415,9 +420,9 @@
@Test
public void testTemperatureWatcherUpdateSevereThresholds() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
watcher.mSevereThresholds.erase();
- watcher.updateSevereThresholds();
+ watcher.updateThresholds();
assertEquals(1, watcher.mSevereThresholds.size());
assertEquals("skin1", watcher.mSevereThresholds.keyAt(0));
Float threshold = watcher.mSevereThresholds.get("skin1");
@@ -426,9 +431,60 @@
}
@Test
+ public void testTemperatureWatcherUpdateHeadroomThreshold() {
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ synchronized (watcher.mSamples) {
+ Arrays.fill(watcher.mHeadroomThresholds, Float.NaN);
+ }
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 40, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 49, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 70, 49);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 79, 49);
+ synchronized (watcher.mSamples) {
+ assertArrayEquals(new float[]{Float.NaN, 0.7f, 0.9f, 1.0f, 1.5f, 1.7f, 2.0f},
+ watcher.mHeadroomThresholds, 0.01f);
+ }
+
+ // when another sensor reports different threshold, we expect to see smaller one to be used
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.LIGHT, 37, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.MODERATE, 46, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SEVERE, 52, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.CRITICAL, 64, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.EMERGENCY, 100, 52);
+ watcher.updateHeadroomThreshold(ThrottlingSeverity.SHUTDOWN, 200, 52);
+ synchronized (watcher.mSamples) {
+ assertArrayEquals(new float[]{Float.NaN, 0.5f, 0.8f, 1.0f, 1.4f, 1.7f, 2.0f},
+ watcher.mHeadroomThresholds, 0.01f);
+ }
+ }
+
+ @Test
+ public void testGetThermalHeadroomThresholdsOnlyReadOnce() throws Exception {
+ float[] expected = new float[]{Float.NaN, 0.1f, 0.2f, 0.3f, 0.4f, Float.NaN, 0.6f};
+ when(mIThermalServiceMock.getThermalHeadroomThresholds()).thenReturn(expected);
+ Map<Integer, Float> thresholds1 = mPowerManager.getThermalHeadroomThresholds();
+ verify(mIThermalServiceMock, times(1)).getThermalHeadroomThresholds();
+ for (int status = PowerManager.THERMAL_STATUS_LIGHT;
+ status <= PowerManager.THERMAL_STATUS_SHUTDOWN; status++) {
+ if (Float.isNaN(expected[status])) {
+ assertFalse(thresholds1.containsKey(status));
+ } else {
+ assertEquals(expected[status], thresholds1.get(status), 0.01f);
+ }
+ }
+ reset(mIThermalServiceMock);
+ Map<Integer, Float> thresholds2 = mPowerManager.getThermalHeadroomThresholds();
+ verify(mIThermalServiceMock, times(0)).getThermalHeadroomThresholds();
+ assertNotSame(thresholds1, thresholds2);
+ assertEquals(thresholds1, thresholds2);
+ }
+
+ @Test
public void testTemperatureWatcherGetSlopeOf() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
- List<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>();
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ List<TemperatureWatcher.Sample> samples = new ArrayList<>();
for (int i = 0; i < 30; ++i) {
samples.add(watcher.createSampleForTesting(i, (float) (i / 2 * 2)));
}
@@ -437,21 +493,23 @@
@Test
public void testTemperatureWatcherNormalizeTemperature() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
- assertEquals(0.5f, watcher.normalizeTemperature(25.0f, 40.0f), 0.0f);
+ assertEquals(0.5f,
+ TemperatureWatcher.normalizeTemperature(25.0f, 40.0f), 0.0f);
// Temperatures more than 30 degrees below the SEVERE threshold should be clamped to 0.0f
- assertEquals(0.0f, watcher.normalizeTemperature(0.0f, 40.0f), 0.0f);
+ assertEquals(0.0f,
+ TemperatureWatcher.normalizeTemperature(0.0f, 40.0f), 0.0f);
// Temperatures above the SEVERE threshold should not be clamped
- assertEquals(2.0f, watcher.normalizeTemperature(70.0f, 40.0f), 0.0f);
+ assertEquals(2.0f,
+ TemperatureWatcher.normalizeTemperature(70.0f, 40.0f), 0.0f);
}
@Test
public void testTemperatureWatcherGetForecast() throws RemoteException {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
- ArrayList<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>();
+ ArrayList<TemperatureWatcher.Sample> samples = new ArrayList<>();
// Add a single sample
samples.add(watcher.createSampleForTesting(0, 25.0f));
@@ -478,7 +536,7 @@
@Test
public void testTemperatureWatcherGetForecastUpdate() throws Exception {
- ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher;
+ TemperatureWatcher watcher = mService.mTemperatureWatcher;
// Reduce the inactivity threshold to speed up testing
watcher.mInactivityThresholdMillis = 2000;
@@ -499,7 +557,7 @@
}
// Helper function to hold mSamples lock, avoid GuardedBy lint errors
- private boolean isWatcherSamplesEmpty(ThermalManagerService.TemperatureWatcher watcher) {
+ private boolean isWatcherSamplesEmpty(TemperatureWatcher watcher) {
synchronized (watcher.mSamples) {
return watcher.mSamples.isEmpty();
}
diff --git a/services/tests/timetests/TEST_MAPPING b/services/tests/timetests/TEST_MAPPING
index b24010c..e549229 100644
--- a/services/tests/timetests/TEST_MAPPING
+++ b/services/tests/timetests/TEST_MAPPING
@@ -1,6 +1,5 @@
{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksTimeServicesTests"
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
index 147a44f..fafc035 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java
@@ -190,7 +190,7 @@
final WindowManagerService wms = mWm;
final DisplayContent displayContent = mock(DisplayContent.class);
doReturn(true).when(displayContent).isTrusted();
- doReturn(true).when(displayContent).supportsSystemDecorations();
+ doReturn(true).when(displayContent).isHomeSupported();
final RootDisplayArea root = new SurfacelessDisplayAreaRoot(wms);
final TaskDisplayArea taskDisplayAreaWithHome = new TaskDisplayArea(displayContent, wms,
"Tasks1", FEATURE_DEFAULT_TASK_CONTAINER);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index c782d3e..be96e60 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -274,6 +274,26 @@
assertEquals(mAppWindow, policy.getTopFullscreenOpaqueWindow());
}
+ @SetupWindows(addWindows = W_NOTIFICATION_SHADE)
+ @Test
+ public void testVisibleProcessWhileDozing() {
+ final WindowProcessController wpc = mNotificationShadeWindow.getProcess();
+ final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ policy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs);
+
+ policy.screenTurnedOff();
+ policy.setAwake(false);
+ policy.screenTurnedOn(null /* screenOnListener */);
+ assertTrue(wpc.isShowingUiWhileDozing());
+ policy.screenTurnedOff();
+ assertFalse(wpc.isShowingUiWhileDozing());
+
+ policy.screenTurnedOn(null /* screenOnListener */);
+ assertTrue(wpc.isShowingUiWhileDozing());
+ policy.setAwake(true);
+ assertFalse(wpc.isShowingUiWhileDozing());
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testMainAppWindowDisallowFitSystemWindowTypes() {
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index e54b8e5..e2524a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -496,6 +496,19 @@
mPrivateDisplay.getDisplayInfo());
}
+ @Test
+ public void testClearDisplaySettings() {
+ spyOn(mWm.mDisplayWindowSettings);
+ spyOn(mWm.mDisplayWindowSettingsProvider);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ DisplayInfo info = mPrivateDisplay.getDisplayInfo();
+ wmInternal.clearDisplaySettings(info.uniqueId, info.type);
+
+ verify(mWm.mDisplayWindowSettings).clearDisplaySettings(info.uniqueId, info.type);
+ verify(mWm.mDisplayWindowSettingsProvider).clearDisplaySettings(info);
+ }
+
public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider {
Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>();
@@ -530,5 +543,10 @@
public void onDisplayRemoved(@NonNull DisplayInfo info) {
mOverrideSettingsCache.remove(info);
}
+
+ @Override
+ public void clearDisplaySettings(@NonNull DisplayInfo info) {
+ mOverrideSettingsCache.remove(info);
+ }
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 013be25..5e531b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -574,6 +574,7 @@
spyOn(root);
spyOn(root.mLetterboxUiController);
+ doReturn(true).when(root).fillsParent();
doReturn(true).when(root.mLetterboxUiController)
.shouldEnableUserAspectRatioSettings();
doReturn(false).when(root).inSizeCompatMode();
@@ -596,6 +597,12 @@
assertFalse(task.getTaskInfo()
.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton);
doReturn(false).when(root).inSizeCompatMode();
+
+ // When the top activity is transparent, the button is not enabled
+ doReturn(false).when(root).fillsParent();
+ assertFalse(task.getTaskInfo()
+ .appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton);
+ doReturn(true).when(root).fillsParent();
}
/**
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 2975e1e..fc7c6a6 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -1231,6 +1231,26 @@
complianceWarningsProto.add(FrameworkStatsLog
.USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_MISSING_RP);
continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_MISSING_DATA_LINES);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_ENUMERATION_FAIL);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_FLAKY_CONNECTION);
+ continue;
+ case UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO:
+ complianceWarningsProto.add(FrameworkStatsLog
+ .USB_COMPLIANCE_WARNINGS_REPORTED__COMPLIANCE_WARNINGS__COMPLIANCE_WARNING_UNRELIABLE_IO);
+ continue;
}
}
return complianceWarningsProto.toArray();
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index edd6597..5059017 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -546,6 +546,8 @@
int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 245;
int RIL_REQUEST_IS_CELLULAR_IDENTIFIER_DISCLOSED_ENABLED = 246;
int RIL_REQUEST_SET_CELLULAR_IDENTIFIER_DISCLOSED_ENABLED = 247;
+ int RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED = 248;
+ int RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED = 249;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
@@ -608,6 +610,7 @@
int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
int RIL_UNSOL_CELLULAR_IDENTIFIER_DISCLOSED = 1056;
+ int RIL_UNSOL_SECURITY_ALGORITHMS_UPDATED = 1057;
/* The following unsols are not defined in RIL.h */
int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
index ae7c2a9..4548a7d 100644
--- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -78,14 +78,15 @@
// TODO(b/293651105): Unhardcode category fps range mapping
private static final FpsRange FRAME_RATE_CATEGORY_HIGH = new FpsRange(90, 120);
private static final FpsRange FRAME_RATE_CATEGORY_NORMAL = new FpsRange(60, 90);
- private static final FpsRange FRAME_RATE_CATEGORY_LOW = new FpsRange(30, 60);
+ private static final FpsRange FRAME_RATE_CATEGORY_LOW = new FpsRange(30, 30);
private DisplayManager mDisplayManager;
private SurfaceView mSurfaceView;
private Handler mHandler = new Handler(Looper.getMainLooper());
private final Object mLock = new Object();
private Surface mSurface = null;
- private float mDeviceFrameRate;
+ private float mDisplayModeRefreshRate;
+ private float mDisplayRefreshRate;
private ModeChangedEvents mModeChangedEvents = new ModeChangedEvents();
private enum ActivityState { RUNNING, PAUSED, DESTROYED }
@@ -123,14 +124,20 @@
return;
}
synchronized (mLock) {
- Display.Mode mode = mDisplayManager.getDisplay(displayId).getMode();
+ Display display = mDisplayManager.getDisplay(displayId);
+ Display.Mode mode = display.getMode();
mModeChangedEvents.add(mode);
- float frameRate = mode.getRefreshRate();
- if (frameRate != mDeviceFrameRate) {
+ float displayModeRefreshRate = mode.getRefreshRate();
+ float displayRefreshRate = display.getRefreshRate();
+ if (displayModeRefreshRate != mDisplayModeRefreshRate
+ || displayRefreshRate != mDisplayRefreshRate) {
Log.i(TAG,
- String.format("Frame rate changed: %.2f --> %.2f", mDeviceFrameRate,
- frameRate));
- mDeviceFrameRate = frameRate;
+ String.format("Refresh rate changed: (mode) %.2f --> %.2f, "
+ + "(display) %.2f --> %.2f",
+ mDisplayModeRefreshRate, displayModeRefreshRate,
+ mDisplayRefreshRate, displayRefreshRate));
+ mDisplayModeRefreshRate = displayModeRefreshRate;
+ mDisplayRefreshRate = displayRefreshRate;
mLock.notify();
}
}
@@ -317,8 +324,10 @@
super.onCreate(savedInstanceState);
synchronized (mLock) {
mDisplayManager = getSystemService(DisplayManager.class);
- Display.Mode mode = getDisplay().getMode();
- mDeviceFrameRate = mode.getRefreshRate();
+ Display display = getDisplay();
+ Display.Mode mode = display.getMode();
+ mDisplayModeRefreshRate = mode.getRefreshRate();
+ mDisplayRefreshRate = display.getRefreshRate();
// Insert the initial mode so we have the full display mode history.
mModeChangedEvents.add(mode);
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
@@ -516,22 +525,25 @@
if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0
// Wait until we switch to a compatible frame rate.
Log.i(TAG,
- "Verifying expected frame rate: actual (device)=" + mDeviceFrameRate
- + " expected=" + expectedFrameRate);
+ String.format(
+ "Verifying expected frame rate: actual=%.2f, expected=%.2f",
+ multiplesAllowed ? mDisplayModeRefreshRate : mDisplayRefreshRate,
+ expectedFrameRate));
if (multiplesAllowed) {
- while (!isFrameRateMultiple(mDeviceFrameRate, expectedFrameRate)
+ while (!isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)
&& !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
// Empty
}
} else {
- while (!frameRateEquals(mDeviceFrameRate, expectedFrameRate)
+ while (!frameRateEquals(mDisplayRefreshRate, expectedFrameRate)
&& !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
// Empty
}
}
nowNanos = System.nanoTime();
if (nowNanos >= gracePeriodEndTimeNanos) {
- throw new FrameRateTimeoutException(expectedFrameRate, mDeviceFrameRate);
+ throw new FrameRateTimeoutException(expectedFrameRate,
+ multiplesAllowed ? mDisplayModeRefreshRate : mDisplayRefreshRate);
}
}
@@ -541,7 +553,10 @@
while (endTimeNanos > nowNanos) {
int numModeChangedEvents = mModeChangedEvents.size();
if (waitForEvents(endTimeNanos, surfaces)) {
- Log.i(TAG, String.format("Stable frame rate %.2f verified", mDeviceFrameRate));
+ Log.i(TAG,
+ String.format("Stable frame rate %.2f verified",
+ multiplesAllowed ? mDisplayModeRefreshRate
+ : mDisplayRefreshRate));
return;
}
nowNanos = System.nanoTime();
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index fd4ec8b..5949bca 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -284,6 +284,9 @@
"hoststubgen-helper-runtime.ravenwood",
"framework-minus-apex.ravenwood",
],
+ static_libs: [
+ "core-xml-for-device",
+ ],
}
// Defaults for host side test modules.
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
index 12c7841..4a3a798 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -286,11 +286,15 @@
}
public static byte[] nativeReadBlob(long nativePtr) {
+ var p = getInstance(nativePtr);
+ if (p.mSize - p.mPos < 4) {
+ // Match native impl that returns "null" when not enough data
+ return null;
+ }
final var size = nativeReadInt(nativePtr);
if (size == -1) {
return null;
}
- var p = getInstance(nativePtr);
try {
p.ensureDataAvailable(align4(size));
} catch (Exception e) {