Merge "Add option to limit what classes can have annotations" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index ab0d5a3..3a772e1 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -61,6 +61,7 @@
":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}",
]
filegroup {
@@ -681,3 +682,16 @@
aconfig_declarations: "android.view.contentcapture.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// USB
+aconfig_declarations {
+ name: "android.hardware.usb.flags-aconfig",
+ package: "android.hardware.usb.flags",
+ srcs: ["core/java/android/hardware/usb/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.usb.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.usb.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 49386d4..895ef98 100644
--- a/Android.bp
+++ b/Android.bp
@@ -399,6 +399,7 @@
"soundtrigger_middleware-aidl-java",
"modules-utils-binary-xml",
"modules-utils-build",
+ "modules-utils-fastxmlserializer",
"modules-utils-preconditions",
"modules-utils-statemachine",
"modules-utils-synchronous-result-receiver",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 1582266..3310898 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -77,10 +77,17 @@
"framework-minus-apex.ravenwood",
"hoststubgen-helper-runtime.ravenwood",
"hoststubgen-helper-framework-runtime.ravenwood",
+ "junit",
+ "truth",
+ "ravenwood-junit",
],
}
android_ravenwood_libgroup {
name: "ravenwood-utils",
- libs: [],
+ libs: [
+ "junit",
+ "truth",
+ "ravenwood-junit",
+ ],
}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 887f7fe..6c83add 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -30,6 +30,10 @@
"unsupportedappusage",
],
+ static_libs: [
+ "modules-utils-fastxmlserializer",
+ ],
+
// Rename classes shared with the framework
jarjar_rules: "jarjar-rules.txt",
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 5fc7745..e08200b 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -23,7 +23,6 @@
import android.app.AppOpsManager.PackageOps;
import android.app.IActivityManager;
import android.app.usage.UsageStatsManager;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -747,10 +746,8 @@
public void opChanged(int op, int uid, String packageName) throws RemoteException {
boolean restricted = false;
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(packageName).build();
- restricted = mAppOpsService.checkOperationWithState(TARGET_OP,
- attributionSource.asState()) != AppOpsManager.MODE_ALLOWED;
+ restricted = mAppOpsService.checkOperation(TARGET_OP,
+ uid, packageName) != AppOpsManager.MODE_ALLOWED;
} catch (RemoteException e) {
// Shouldn't happen
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 95f901c..b8397d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -38,7 +38,6 @@
import android.app.tare.IEconomyManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -231,11 +230,8 @@
public void opChanged(int op, int uid, String packageName) {
boolean restricted = false;
try {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .build();
- restricted = mAppOpsService.checkOperationWithState(
- AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, attributionSource.asState())
+ restricted = mAppOpsService.checkOperation(
+ AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName)
!= AppOpsManager.MODE_ALLOWED;
} catch (RemoteException e) {
// Shouldn't happen
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 7d38377..12f455a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -2539,6 +2539,38 @@
pw.println("]");
pw.println();
+ pw.println("mActiveAdminApps=[");
+ synchronized (mActiveAdminApps) {
+ final int size = mActiveAdminApps.size();
+ for (int i = 0; i < size; ++i) {
+ final int userId = mActiveAdminApps.keyAt(i);
+ pw.print(" ");
+ pw.print(userId);
+ pw.print(": ");
+ pw.print(mActiveAdminApps.valueAt(i));
+ if (i != size - 1) pw.print(",");
+ pw.println();
+ }
+ }
+ pw.println("]");
+ pw.println();
+
+ pw.println("mAdminProtectedPackages=[");
+ synchronized (mAdminProtectedPackages) {
+ final int size = mAdminProtectedPackages.size();
+ for (int i = 0; i < size; ++i) {
+ final int userId = mAdminProtectedPackages.keyAt(i);
+ pw.print(" ");
+ pw.print(userId);
+ pw.print(": ");
+ pw.print(mAdminProtectedPackages.valueAt(i));
+ if (i != size - 1) pw.print(",");
+ pw.println();
+ }
+ }
+ pw.println("]");
+ pw.println();
+
mInjector.dump(pw);
}
diff --git a/cmds/gpu_counter_producer/main.cpp b/cmds/gpu_counter_producer/main.cpp
index 1054cba..4616638 100644
--- a/cmds/gpu_counter_producer/main.cpp
+++ b/cmds/gpu_counter_producer/main.cpp
@@ -133,6 +133,12 @@
daemon(0, 0);
}
+ if (getenv("LD_LIBRARY_PATH") == nullptr) {
+ setenv("LD_LIBRARY_PATH", "/vendor/lib64:/vendor/lib", 0 /*override*/);
+ LOG_INFO("execv with: LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
+ execvpe(pname, argv, environ);
+ }
+
if (!writeToPidFile()) {
LOG_ERR("Could not open %s", kPidFileName);
return 1;
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index ee9c464..2d23533 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -142,13 +142,22 @@
case 'p':
png = true;
break;
- case 'd':
- displayIdOpt = DisplayId::fromValue(atoll(optarg));
+ case 'd': {
+ errno = 0;
+ char* end = nullptr;
+ const uint64_t id = strtoull(optarg, &end, 10);
+ if (!end || *end != '\0' || errno == ERANGE) {
+ fprintf(stderr, "Invalid display ID: Out of range [0, 2^64).\n");
+ return 1;
+ }
+
+ displayIdOpt = DisplayId::fromValue(id);
if (!displayIdOpt) {
- fprintf(stderr, "Invalid display ID: %s\n", optarg);
+ fprintf(stderr, "Invalid display ID: Incorrect encoding.\n");
return 1;
}
break;
+ }
case '?':
case 'h':
if (ids.size() == 1) {
diff --git a/core/api/current.txt b/core/api/current.txt
index e8a6ac9..323f9e9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4464,6 +4464,7 @@
method public boolean onPreparePanel(int, @Nullable android.view.View, @NonNull android.view.Menu);
method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int);
@@ -5315,11 +5316,15 @@
method public android.net.Uri getConditionId();
method @Nullable public android.content.ComponentName getConfigurationActivity();
method public long getCreationTime();
+ method @FlaggedApi("android.app.modes_api") @DrawableRes public int getIconResId();
method public int getInterruptionFilter();
method public String getName();
method public android.content.ComponentName getOwner();
+ method @FlaggedApi("android.app.modes_api") @Nullable public String getTriggerDescription();
+ method @FlaggedApi("android.app.modes_api") public int getType();
method public android.service.notification.ZenPolicy getZenPolicy();
method public boolean isEnabled();
+ method @FlaggedApi("android.app.modes_api") public boolean isManualInvocationAllowed();
method public void setConditionId(android.net.Uri);
method public void setConfigurationActivity(@Nullable android.content.ComponentName);
method public void setEnabled(boolean);
@@ -5328,6 +5333,32 @@
method public void setZenPolicy(android.service.notification.ZenPolicy);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_BEDTIME = 3; // 0x3
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_DRIVING = 4; // 0x4
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_IMMERSIVE = 5; // 0x5
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_MANAGED = 7; // 0x7
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_OTHER = 0; // 0x0
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_SCHEDULE_CALENDAR = 2; // 0x2
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_SCHEDULE_TIME = 1; // 0x1
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_THEATER = 6; // 0x6
+ field @FlaggedApi("android.app.modes_api") public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+ }
+
+ @FlaggedApi("android.app.modes_api") public static final class AutomaticZenRule.Builder {
+ ctor public AutomaticZenRule.Builder(@NonNull android.app.AutomaticZenRule);
+ ctor public AutomaticZenRule.Builder(@NonNull String, @NonNull android.net.Uri);
+ method @NonNull public android.app.AutomaticZenRule build();
+ method @NonNull public android.app.AutomaticZenRule.Builder setConditionId(@NonNull android.net.Uri);
+ method @NonNull public android.app.AutomaticZenRule.Builder setConfigurationActivity(@Nullable android.content.ComponentName);
+ method @NonNull public android.app.AutomaticZenRule.Builder setEnabled(boolean);
+ method @NonNull public android.app.AutomaticZenRule.Builder setIconResId(@DrawableRes int);
+ method @NonNull public android.app.AutomaticZenRule.Builder setInterruptionFilter(int);
+ method @NonNull public android.app.AutomaticZenRule.Builder setManualInvocationAllowed(boolean);
+ method @NonNull public android.app.AutomaticZenRule.Builder setName(@NonNull String);
+ method @NonNull public android.app.AutomaticZenRule.Builder setOwner(@Nullable android.content.ComponentName);
+ method @NonNull public android.app.AutomaticZenRule.Builder setTriggerDescription(@Nullable String);
+ method @NonNull public android.app.AutomaticZenRule.Builder setType(int);
+ method @NonNull public android.app.AutomaticZenRule.Builder setZenPolicy(@Nullable android.service.notification.ZenPolicy);
}
public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable {
@@ -13943,6 +13974,7 @@
method public String getColumnName(int);
method public android.os.Bundle getExtras();
method public android.net.Uri getNotificationUri();
+ method public java.util.List<android.net.Uri> getNotificationUris();
method public final int getPosition();
method public int getType(int);
method @Deprecated protected Object getUpdatedField(int);
@@ -13968,6 +14000,7 @@
method public android.os.Bundle respond(android.os.Bundle);
method public void setExtras(android.os.Bundle);
method public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method public void setNotificationUris(@NonNull android.content.ContentResolver, @NonNull java.util.List<android.net.Uri>);
method public void unregisterContentObserver(android.database.ContentObserver);
method public void unregisterDataSetObserver(android.database.DataSetObserver);
field @Deprecated protected boolean mClosed;
@@ -14099,6 +14132,7 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
+ method public void remove();
}
public enum CursorJoiner.Result {
@@ -14166,6 +14200,7 @@
method public int getInt(int);
method public long getLong(int);
method public android.net.Uri getNotificationUri();
+ method public java.util.List<android.net.Uri> getNotificationUris();
method public int getPosition();
method public short getShort(int);
method public String getString(int);
@@ -14190,6 +14225,7 @@
method public android.os.Bundle respond(android.os.Bundle);
method public void setExtras(android.os.Bundle);
method public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>);
method public void unregisterContentObserver(android.database.ContentObserver);
method public void unregisterDataSetObserver(android.database.DataSetObserver);
}
@@ -18634,7 +18670,7 @@
method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
method @Nullable public javax.crypto.Mac getMac();
- method @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public long getOpId();
+ method @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public long getOperationHandle();
method @Nullable public android.security.identity.PresentationSession getPresentationSession();
method @Nullable public java.security.Signature getSignature();
}
@@ -22183,6 +22219,9 @@
ctor public MediaCodec.CryptoException(int, @Nullable String);
method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo();
method public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
field @Deprecated public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
@@ -22695,6 +22734,9 @@
public final class MediaCryptoException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaCryptoException(@Nullable String);
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
}
public abstract class MediaDataSource implements java.io.Closeable {
@@ -22919,6 +22961,9 @@
public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
method @NonNull public String getDiagnosticInfo();
method public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
method public boolean isTransient();
}
@@ -22992,6 +23037,9 @@
public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
method public boolean isTransient();
field @Deprecated public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0
@@ -22999,6 +23047,9 @@
public class MediaDrmException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaDrmException(String);
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
}
public class MediaDrmResetException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
@@ -31888,6 +31939,7 @@
method public void surfaceCreated(android.view.SurfaceHolder);
method public void surfaceDestroyed(android.view.SurfaceHolder);
method @Deprecated public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public void surfaceRedrawNeededAsync(android.view.SurfaceHolder, Runnable);
field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -47429,6 +47481,7 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public String next();
+ method public void remove();
method public void setString(String);
}
@@ -48894,6 +48947,7 @@
method public void ensureCapacity(int);
method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
method public boolean equals(@Nullable Object);
+ method public void forEach(java.util.function.BiConsumer<? super K,? super V>);
method public V get(Object);
method public int hashCode();
method public int indexOfKey(Object);
@@ -48907,6 +48961,7 @@
method public V remove(Object);
method public boolean removeAll(java.util.Collection<?>);
method public V removeAt(int);
+ method public void replaceAll(java.util.function.BiFunction<? super K,? super V,? extends V>);
method public boolean retainAll(java.util.Collection<?>);
method public V setValueAt(int, V);
method public int size();
@@ -48937,6 +48992,7 @@
method public boolean removeAll(android.util.ArraySet<? extends E>);
method public boolean removeAll(java.util.Collection<?>);
method public E removeAt(int);
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public boolean retainAll(java.util.Collection<?>);
method public int size();
method public Object[] toArray();
@@ -52804,6 +52860,7 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(@NonNull android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
+ method @Nullable public android.window.OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(@NonNull android.view.View, @NonNull android.view.View);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams();
@@ -52845,6 +52902,7 @@
method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect);
method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect);
+ method @CallSuper public void onDescendantInvalidated(@NonNull android.view.View, @NonNull android.view.View);
method public boolean onInterceptHoverEvent(android.view.MotionEvent);
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
@@ -55028,12 +55086,14 @@
method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method @Nullable public android.os.Handler getHandler();
method @Nullable public CharSequence getSelectedText(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public static final void removeComposingSpans(@NonNull android.text.Spannable);
+ method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
@@ -55041,6 +55101,7 @@
method public static void setComposingSpans(@NonNull android.text.Spannable);
method public boolean setComposingText(CharSequence, int);
method public boolean setSelection(int, int);
+ method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot();
}
public final class CompletionInfo implements android.os.Parcelable {
@@ -55377,6 +55438,7 @@
method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(CharSequence, int);
+ method public boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean deleteSurroundingText(int, int);
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
@@ -55385,18 +55447,29 @@
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(int, int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
+ method public void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
+ method public boolean performSpellCheck();
+ method public boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal);
+ method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
+ method public boolean requestCursorUpdates(int, int);
+ method public void requestTextBoundsInfo(@NonNull android.graphics.RectF, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inputmethod.TextBoundsInfoResult>);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
+ method public boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean setComposingText(CharSequence, int);
+ method public boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
+ method public boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
method public void setTarget(android.view.inputmethod.InputConnection);
+ method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot();
}
public final class InputContentInfo implements android.os.Parcelable {
@@ -57963,6 +58036,7 @@
public abstract class BaseAdapter implements android.widget.ListAdapter android.widget.SpinnerAdapter {
ctor public BaseAdapter();
method public boolean areAllItemsEnabled();
+ method public CharSequence[] getAutofillOptions();
method public android.view.View getDropDownView(int, android.view.View, android.view.ViewGroup);
method public int getItemViewType(int);
method public int getViewTypeCount();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 0737496..df466ab 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -144,7 +144,6 @@
}
public abstract class PackageManager {
- method @NonNull public String getPermissionControllerPackageName();
method @NonNull public String getSdkSandboxPackageName();
method @RequiresPermission(android.Manifest.permission.MAKE_UID_VISIBLE) public void makeUidVisible(int, int);
field public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.VERIFICATION_ROOT_HASH";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 441dcae..eefa36e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -133,6 +133,7 @@
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String GET_APP_METADATA = "android.permission.GET_APP_METADATA";
field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
+ field @FlaggedApi("android.app.get_binding_uid_importance") public static final String GET_BINDING_UID_IMPORTANCE = "android.permission.GET_BINDING_UID_IMPORTANCE";
field public static final String GET_HISTORICAL_APP_OPS_STATS = "android.permission.GET_HISTORICAL_APP_OPS_STATS";
field public static final String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final String GET_RUNTIME_PERMISSIONS = "android.permission.GET_RUNTIME_PERMISSIONS";
@@ -367,6 +368,7 @@
field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE";
field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE";
@@ -543,6 +545,7 @@
public class ActivityManager {
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
+ method @FlaggedApi("android.app.get_binding_uid_importance") @RequiresPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE) public int getBindingUidImportance(int);
method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
method @FlaggedApi("android.app.app_start_info") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
@@ -3913,6 +3916,7 @@
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public int getPackageUidAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public String getPermissionControllerPackageName();
method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
@@ -6186,8 +6190,13 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final int COMPLIANCE_WARNING_BC_1_2 = 3; // 0x3
field public static final int COMPLIANCE_WARNING_DEBUG_ACCESSORY = 2; // 0x2
+ field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_ENUMERATION_FAIL = 7; // 0x7
+ field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_FLAKY_CONNECTION = 8; // 0x8
+ field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_INPUT_POWER_LIMITED = 5; // 0x5
+ field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_MISSING_DATA_LINES = 6; // 0x6
field public static final int COMPLIANCE_WARNING_MISSING_RP = 4; // 0x4
field public static final int COMPLIANCE_WARNING_OTHER = 1; // 0x1
+ field @FlaggedApi("android.hardware.usb.flags.enable_usb_data_compliance_warning") public static final int COMPLIANCE_WARNING_UNRELIABLE_IO = 9; // 0x9
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.UsbPortStatus> CREATOR;
field public static final int DATA_ROLE_DEVICE = 2; // 0x2
field public static final int DATA_ROLE_HOST = 1; // 0x1
@@ -9725,6 +9734,7 @@
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOtherServiceEnabled();
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
@@ -9735,6 +9745,7 @@
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOtherServiceEnabled(boolean);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
}
@@ -10217,6 +10228,7 @@
ctor public ParcelableHolder(int);
method public int describeContents();
method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>);
+ method public int getStability();
method public void readFromParcel(@NonNull android.os.Parcel);
method public void setParcelable(@Nullable android.os.Parcelable);
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b2bfda1..8b20720 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3592,8 +3592,8 @@
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
field public CharSequence accessibilityTitle;
- field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMaxDisplayRefreshRate;
- field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMinDisplayRefreshRate;
+ field public float preferredMaxDisplayRefreshRate;
+ field public float preferredMinDisplayRefreshRate;
field public int privateFlags;
}
diff --git a/core/java/Android.bp b/core/java/Android.bp
index ddb221f..48cafc5 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -226,7 +226,6 @@
"com/android/internal/util/ConcurrentUtils.java",
"com/android/internal/util/DumpUtils.java",
"com/android/internal/util/FastPrintWriter.java",
- "com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/FunctionalUtils.java",
"com/android/internal/util/ParseUtils.java",
"com/android/internal/util/RingBufferIndices.java",
@@ -465,7 +464,6 @@
"com/android/internal/util/AsyncChannel.java",
"com/android/internal/util/AsyncService.java",
"com/android/internal/util/BitwiseInputStream.java",
- "com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/HexDump.java",
"com/android/internal/util/IndentingPrintWriter.java",
"com/android/internal/util/UserIcons.java",
@@ -515,7 +513,6 @@
"android/net/InterfaceConfiguration.java",
"android/util/BackupUtils.java",
"android/util/Rational.java",
- "com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/HexDump.java",
"com/android/internal/util/MessageUtils.java",
"com/android/internal/util/WakeupMessage.java",
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f68681b..8b4ebae 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -73,7 +73,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -4269,6 +4268,33 @@
}
/**
+ * Same as {@link #getUidImportance(int)}, but it only works on UIDs that currently
+ * have a service binding, or provider reference, to the calling UID, even if the target UID
+ * belong to another android user or profile.
+ *
+ * <p>This will return {@link RunningAppProcessInfo#IMPORTANCE_GONE} on all other UIDs,
+ * regardless of if they're valid or not.
+ *
+ * <p>Privileged system apps may prefer this API to {@link #getUidImportance(int)} to
+ * avoid requesting the permission {@link Manifest.permission#PACKAGE_USAGE_STATS}, which
+ * would allow access to APIs that return more senstive information.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_GET_BINDING_UID_IMPORTANCE)
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GET_BINDING_UID_IMPORTANCE)
+ public @RunningAppProcessInfo.Importance int getBindingUidImportance(int uid) {
+ try {
+ int procState = getService().getBindingUidProcessState(uid,
+ mContext.getOpPackageName());
+ return RunningAppProcessInfo.procStateToImportanceForClient(procState, mContext);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Callback to get reports about changes to the importance of a uid. Use with
* {@link #addOnUidImportanceListener}.
* @hide
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dfb416a..c136db6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -56,7 +56,7 @@
import android.app.backup.BackupAnnotations.OperationType;
import android.app.compat.CompatChanges;
import android.app.sdksandbox.sandboxactivity.ActivityContextInfo;
-import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority;
+import android.app.sdksandbox.sandboxactivity.ActivityContextInfoProvider;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
import android.app.servertransaction.ActivityRelaunchItem;
@@ -378,6 +378,15 @@
/** Maps from activity token to the pending override configuration. */
@GuardedBy("mPendingOverrideConfigs")
private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>();
+
+ /**
+ * A queue of pending ApplicationInfo updates. In case when we get a concurrent update
+ * this queue allows us to only apply the latest object, and it can be applied on demand
+ * instead of waiting for the handler thread to reach the scheduled callback.
+ */
+ @GuardedBy("mResourcesManager")
+ private final ArrayMap<String, ApplicationInfo> mPendingAppInfoUpdates = new ArrayMap<>();
+
/** The activities to be truly destroyed (not include relaunch). */
final Map<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed =
Collections.synchronizedMap(new ArrayMap<>());
@@ -1326,9 +1335,19 @@
}
public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
+ synchronized (mResourcesManager) {
+ var oldAi = mPendingAppInfoUpdates.put(ai.packageName, ai);
+ if (oldAi != null && oldAi.createTimestamp > ai.createTimestamp) {
+ Slog.w(TAG, "Skipping application info changed for obsolete AI with TS "
+ + ai.createTimestamp + " < already pending TS "
+ + oldAi.createTimestamp);
+ mPendingAppInfoUpdates.put(ai.packageName, oldAi);
+ return;
+ }
+ }
mResourcesManager.appendPendingAppInfoUpdate(new String[]{ai.sourceDir}, ai);
- mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai);
- sendMessage(H.APPLICATION_INFO_CHANGED, ai);
+ mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai.packageName);
+ sendMessage(H.APPLICATION_INFO_CHANGED, ai.packageName);
}
public void updateTimeZone() {
@@ -2272,8 +2291,7 @@
case DUMP_HEAP: return "DUMP_HEAP";
case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
- case UPDATE_PACKAGE_COMPATIBILITY_INFO:
- return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
+ case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
case DUMP_PROVIDER: return "DUMP_PROVIDER";
case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS";
@@ -2508,7 +2526,7 @@
break;
}
case APPLICATION_INFO_CHANGED:
- handleApplicationInfoChanged((ApplicationInfo) msg.obj);
+ applyPendingApplicationInfoChanges((String) msg.obj);
break;
case RUN_ISOLATED_ENTRY_POINT:
handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,
@@ -3777,10 +3795,8 @@
r.activityInfo.targetActivity);
}
- boolean isSandboxActivityContext =
- sandboxActivitySdkBasedContext()
- && SdkSandboxActivityAuthority.isSdkSandboxActivity(
- mSystemContext, r.intent);
+ boolean isSandboxActivityContext = sandboxActivitySdkBasedContext()
+ && r.intent.isSandboxActivity(mSystemContext);
boolean isSandboxedSdkContextUsed = false;
ContextImpl activityBaseContext;
if (isSandboxActivityContext) {
@@ -4025,12 +4041,11 @@
*/
@Nullable
private ContextImpl createBaseContextForSandboxActivity(@NonNull ActivityClientRecord r) {
- SdkSandboxActivityAuthority sdkSandboxActivityAuthority =
- SdkSandboxActivityAuthority.getInstance();
+ ActivityContextInfoProvider contextInfoProvider = ActivityContextInfoProvider.getInstance();
ActivityContextInfo contextInfo;
try {
- contextInfo = sdkSandboxActivityAuthority.getActivityContextInfo(r.intent);
+ contextInfo = contextInfoProvider.getActivityContextInfo(r.intent);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Passed intent does not match an expected sandbox activity", e);
return null;
@@ -4074,7 +4089,8 @@
mProfiler.startProfiling();
}
- // Make sure we are running with the most recent config.
+ // Make sure we are running with the most recent config and resource paths.
+ applyPendingApplicationInfoChanges(r.activityInfo.packageName);
mConfigurationController.handleConfigurationChanged(null, null);
updateDeviceIdForNonUIContexts(deviceId);
@@ -6442,6 +6458,17 @@
r.mLastReportedWindowingMode = newWindowingMode;
}
+ private void applyPendingApplicationInfoChanges(String packageName) {
+ final ApplicationInfo ai;
+ synchronized (mResourcesManager) {
+ ai = mPendingAppInfoUpdates.remove(packageName);
+ }
+ if (ai == null) {
+ return;
+ }
+ handleApplicationInfoChanged(ai);
+ }
+
/**
* Updates the application info.
*
@@ -6467,6 +6494,16 @@
apk = ref != null ? ref.get() : null;
ref = mResourcePackages.get(ai.packageName);
resApk = ref != null ? ref.get() : null;
+ for (ActivityClientRecord ar : mActivities.values()) {
+ if (ar.activityInfo.applicationInfo.packageName.equals(ai.packageName)) {
+ ar.activityInfo.applicationInfo = ai;
+ if (apk != null || resApk != null) {
+ ar.packageInfo = apk != null ? apk : resApk;
+ } else {
+ apk = ar.packageInfo;
+ }
+ }
+ }
}
if (apk != null) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a99dfa6..3d54449 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8357,9 +8357,7 @@
*/
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(packageName).build();
- return mService.checkOperationWithStateRaw(op, attributionSource.asState());
+ return mService.checkOperationRaw(op, uid, packageName, null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8522,12 +8520,7 @@
}
}
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .setAttributionTag(attributionTag)
- .build();
- SyncNotedAppOp syncOp = mService.noteOperationWithState(op, attributionSource.asState(),
+ SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
if (syncOp.getOpMode() == MODE_ALLOWED) {
@@ -8767,9 +8760,7 @@
@UnsupportedAppUsage
public int checkOp(int op, int uid, String packageName) {
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(packageName).build();
- int mode = mService.checkOperationWithState(op, attributionSource.asState());
+ int mode = mService.checkOperation(op, uid, packageName);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
@@ -8790,9 +8781,7 @@
@UnsupportedAppUsage
public int checkOpNoThrow(int op, int uid, String packageName) {
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(packageName).build();
- int mode = mService.checkOperationWithState(op, attributionSource.asState());
+ int mode = mService.checkOperation(op, uid, packageName);
return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -9037,14 +9026,8 @@
}
}
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .setAttributionTag(attributionTag)
- .build();
- SyncNotedAppOp syncOp = mService.startOperationWithState(token, op,
- attributionSource.asState(), startIfModeDefault,
- collectionMode == COLLECT_ASYNC, message,
+ SyncNotedAppOp syncOp = mService.startOperation(token, op, uid, packageName,
+ attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
shouldCollectMessage, attributionFlags, attributionChainId);
if (syncOp.getOpMode() == MODE_ALLOWED) {
@@ -9257,12 +9240,7 @@
public void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
@Nullable String attributionTag) {
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .setAttributionTag(attributionTag)
- .build();
- mService.finishOperationWithState(token, op, attributionSource.asState());
+ mService.finishOperation(token, op, uid, packageName, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index a3de8fa..43023fe 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -26,12 +26,11 @@
import android.util.SparseIntArray;
import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintConsumer;
import com.android.internal.util.function.QuintFunction;
-import com.android.internal.util.function.TriConsumer;
-import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
/**
@@ -46,13 +45,15 @@
* Allows overriding check operation behavior.
*
* @param code The op code to check.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param uid The UID for which to check.
+ * @param packageName The package for which to check.
+ * @param attributionTag The attribution tag for which to check.
* @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
* @param superImpl The super implementation.
* @return The app op check result.
*/
- int checkOperation(int code, AttributionSource attributionSource,
- boolean raw, TriFunction<Integer, AttributionSource, Boolean, Integer>
+ int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag,
+ boolean raw, QuintFunction<Integer, Integer, String, String, Boolean, Integer>
superImpl);
/**
@@ -72,23 +73,25 @@
* Allows overriding note operation behavior.
*
* @param code The op code to note.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param uid The UID for which to note.
+ * @param packageName The package for which to note. {@code null} for system package.
+ * @param featureId Id of the feature in the package
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
* @param superImpl The super implementation.
* @return The app op note result.
*/
- SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
- boolean shouldCollectAsyncNotedOp,
+ SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
+ @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
- @NonNull QuintFunction<Integer, AttributionSource, Boolean, String, Boolean,
+ @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
SyncNotedAppOp> superImpl);
/**
* Allows overriding note proxy operation behavior.
*
* @param code The op code to note.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param attributionSource The permission identity of the caller.
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
* @param shouldCollectMessage whether to collect messages
@@ -107,7 +110,9 @@
*
* @param token The client state.
* @param code The op code to start.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param uid The UID for which to note.
+ * @param packageName The package for which to note. {@code null} for system package.
+ * @param attributionTag the attribution tag.
* @param startIfModeDefault Whether to start the op of the mode is default.
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
@@ -117,12 +122,12 @@
* @param superImpl The super implementation.
* @return The app op note result.
*/
- SyncNotedAppOp startOperation(IBinder token, int code,
- AttributionSource attributionSource,
+ SyncNotedAppOp startOperation(IBinder token, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
@AttributionFlags int attributionFlags, int attributionChainId,
- @NonNull NonaFunction<IBinder, Integer, AttributionSource, Boolean,
+ @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl);
/**
@@ -130,7 +135,7 @@
*
* @param clientId The client calling start, represented by an IBinder
* @param code The op code to start.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param attributionSource The permission identity of the caller.
* @param startIfModeDefault Whether to start the op of the mode is default.
* @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
* @param message The message in the async noted op
@@ -156,19 +161,21 @@
*
* @param clientId The client state.
* @param code The op code to finish.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param uid The UID for which the op was noted.
+ * @param packageName The package for which it was noted. {@code null} for system package.
+ * @param attributionTag the attribution tag.
*/
- default void finishOperation(IBinder clientId, int code,
- AttributionSource attributionSource,
- @NonNull TriConsumer<IBinder, Integer, AttributionSource> superImpl) {
- superImpl.accept(clientId, code, attributionSource);
+ default void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag,
+ @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
+ superImpl.accept(clientId, code, uid, packageName, attributionTag);
}
/**
* Allows overriding finish proxy op.
*
* @param code The op code to finish.
- * @param attributionSource the {@link AttributionSource} responsible for data access
+ * @param attributionSource The permission identity of the caller.
* @param skipProxyOperation Whether to skip the proxy in the proxy/proxied operation
* @param clientId The client calling finishProxyOperation
* @param superImpl The "standard" implementation to potentially call
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 7bfb1b5..919e084 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -16,16 +16,23 @@
package android.app;
+import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.NotificationManager.InterruptionFilter;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.notification.Condition;
import android.service.notification.ZenPolicy;
+import android.view.WindowInsetsController;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -36,7 +43,67 @@
private static final int ENABLED = 1;
/* @hide */
private static final int DISABLED = 0;
- private boolean enabled = false;
+
+ /**
+ * Rule is of an unknown type. This is the default value if not provided by the owning app.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_UNKNOWN = -1;
+ /**
+ * Rule is of a known type, but not one of the specific types.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_OTHER = 0;
+ /**
+ * The type for rules triggered according to a time-based schedule.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_SCHEDULE_TIME = 1;
+ /**
+ * The type for rules triggered by calendar events.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_SCHEDULE_CALENDAR = 2;
+ /**
+ * The type for rules triggered by bedtime/sleeping, like time of day, or snore detection.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_BEDTIME = 3;
+ /**
+ * The type for rules triggered by driving detection, like Bluetooth connections or vehicle
+ * sounds.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_DRIVING = 4;
+ /**
+ * The type for rules triggered by the user entering an immersive activity, like opening an app
+ * using {@link WindowInsetsController#hide(int)}.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_IMMERSIVE = 5;
+ /**
+ * The type for rules that have a {@link ZenPolicy} that implies that the
+ * device should not make sound and potentially hide some visual effects; may be triggered
+ * when entering a location where silence is requested, like a theater.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_THEATER = 6;
+ /**
+ * The type for rules created and managed by a device owner. These rules may not be fully
+ * editable by the device user.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final int TYPE_MANAGED = 7;
+
+ /** @hide */
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME,
+ TYPE_DRIVING, TYPE_IMMERSIVE, TYPE_THEATER, TYPE_MANAGED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ private boolean enabled;
private String name;
private @InterruptionFilter int interruptionFilter;
private Uri conditionId;
@@ -46,6 +113,10 @@
private ZenPolicy mZenPolicy;
private boolean mModified = false;
private String mPkg;
+ private int mType = TYPE_UNKNOWN;
+ private int mIconResId;
+ private String mTriggerDescription;
+ private boolean mAllowManualInvocation;
/**
* The maximum string length for any string contained in this automatic zen rule. This pertains
@@ -55,6 +126,12 @@
public static final int MAX_STRING_LENGTH = 1000;
/**
+ * The maximum string length for the trigger description rule, given UI constraints.
+ * @hide
+ */
+ public static final int MAX_DESC_LENGTH = 150;
+
+ /**
* Creates an automatic zen rule.
*
* @param name The name of the rule.
@@ -97,6 +174,7 @@
* action ({@link Condition#STATE_TRUE}).
* @param enabled Whether the rule is enabled.
*/
+ // TODO (b/309088420): deprecate this constructor in favor of the builder
public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner,
@Nullable ComponentName configurationActivity, @NonNull Uri conditionId,
@Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) {
@@ -134,6 +212,12 @@
mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
mModified = source.readInt() == ENABLED;
mPkg = source.readString();
+ if (Flags.modesApi()) {
+ mAllowManualInvocation = source.readBoolean();
+ mIconResId = source.readInt();
+ mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
+ mType = source.readInt();
+ }
}
/**
@@ -269,6 +353,81 @@
return mPkg;
}
+ /**
+ * Gets the type of the rule.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of the rule.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public void setType(@Type int type) {
+ mType = type;
+ }
+
+ /**
+ * Gets the user visible description of when this rule is active
+ * (see {@link Condition#STATE_TRUE}).
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public @Nullable String getTriggerDescription() {
+ return mTriggerDescription;
+ }
+
+ /**
+ * Sets a user visible description of when this rule will be active
+ * (see {@link Condition#STATE_TRUE}).
+ *
+ * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
+ * "When connected to [Car Name]".
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public void setTriggerDescription(@Nullable String triggerDescription) {
+ mTriggerDescription = triggerDescription;
+ }
+
+ /**
+ * Gets the resource id of the drawable icon for this rule.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public @DrawableRes int getIconResId() {
+ return mIconResId;
+ }
+
+ /**
+ * Sets a resource id of a tintable vector drawable representing the rule in image form.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public void setIconResId(int iconResId) {
+ mIconResId = iconResId;
+ }
+
+ /**
+ * Gets whether this rule can be manually activated by the user even when the triggering
+ * condition for the rule is not met.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public boolean isManualInvocationAllowed() {
+ return mAllowManualInvocation;
+ }
+
+ /**
+ * Sets whether this rule can be manually activated by the user even when the triggering
+ * condition for the rule is not met.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public void setManualInvocationAllowed(boolean allowManualInvocation) {
+ mAllowManualInvocation = allowManualInvocation;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -291,11 +450,17 @@
dest.writeParcelable(mZenPolicy, 0);
dest.writeInt(mModified ? ENABLED : DISABLED);
dest.writeString(mPkg);
+ if (Flags.modesApi()) {
+ dest.writeBoolean(mAllowManualInvocation);
+ dest.writeInt(mIconResId);
+ dest.writeString(mTriggerDescription);
+ dest.writeInt(mType);
+ }
}
@Override
public String toString() {
- return new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[')
+ StringBuilder sb = new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[')
.append("enabled=").append(enabled)
.append(",name=").append(name)
.append(",interruptionFilter=").append(interruptionFilter)
@@ -304,8 +469,16 @@
.append(",owner=").append(owner)
.append(",configActivity=").append(configurationActivity)
.append(",creationTime=").append(creationTime)
- .append(",mZenPolicy=").append(mZenPolicy)
- .append(']').toString();
+ .append(",mZenPolicy=").append(mZenPolicy);
+
+ if (Flags.modesApi()) {
+ sb.append(",allowManualInvocation=").append(mAllowManualInvocation)
+ .append(",iconResId=").append(mIconResId)
+ .append(",triggerDescription=").append(mTriggerDescription)
+ .append(",type=").append(mType);
+ }
+
+ return sb.append(']').toString();
}
@Override
@@ -313,7 +486,7 @@
if (!(o instanceof AutomaticZenRule)) return false;
if (o == this) return true;
final AutomaticZenRule other = (AutomaticZenRule) o;
- return other.enabled == enabled
+ boolean finalEquals = other.enabled == enabled
&& other.mModified == mModified
&& Objects.equals(other.name, name)
&& other.interruptionFilter == interruptionFilter
@@ -323,10 +496,23 @@
&& Objects.equals(other.configurationActivity, configurationActivity)
&& Objects.equals(other.mPkg, mPkg)
&& other.creationTime == creationTime;
+ if (Flags.modesApi()) {
+ return finalEquals
+ && other.mAllowManualInvocation == mAllowManualInvocation
+ && other.mIconResId == mIconResId
+ && Objects.equals(other.mTriggerDescription, mTriggerDescription)
+ && other.mType == mType;
+ }
+ return finalEquals;
}
@Override
public int hashCode() {
+ if (Flags.modesApi()) {
+ return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
+ configurationActivity, mZenPolicy, mModified, creationTime, mPkg,
+ mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
+ }
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
}
@@ -357,8 +543,12 @@
* Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
*/
private static String getTrimmedString(String input) {
- if (input != null && input.length() > MAX_STRING_LENGTH) {
- return input.substring(0, MAX_STRING_LENGTH);
+ return getTrimmedString(input, MAX_STRING_LENGTH);
+ }
+
+ private static String getTrimmedString(String input, int length) {
+ if (input != null && input.length() > length) {
+ return input.substring(0, length);
}
return input;
}
@@ -373,4 +563,138 @@
}
return input;
}
+
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ public static final class Builder {
+ private String mName;
+ private ComponentName mOwner;
+ private Uri mConditionId;
+ private int mInterruptionFilter;
+ private boolean mEnabled;
+ private ComponentName mConfigurationActivity = null;
+ private ZenPolicy mPolicy = null;
+ private int mType;
+ private String mDescription;
+ private int mIconResId;
+ private boolean mAllowManualInvocation;
+ private long mCreationTime;
+ private String mPkg;
+
+ public Builder(@NonNull AutomaticZenRule rule) {
+ mName = rule.getName();
+ mOwner = rule.getOwner();
+ mConditionId = rule.getConditionId();
+ mInterruptionFilter = rule.getInterruptionFilter();
+ mEnabled = rule.isEnabled();
+ mConfigurationActivity = rule.getConfigurationActivity();
+ mPolicy = rule.getZenPolicy();
+ mType = rule.getType();
+ mDescription = rule.getTriggerDescription();
+ mIconResId = rule.getIconResId();
+ mAllowManualInvocation = rule.isManualInvocationAllowed();
+ mCreationTime = rule.getCreationTime();
+ mPkg = rule.getPackageName();
+ }
+
+ public Builder(@NonNull String name, @NonNull Uri conditionId) {
+ mName = name;
+ mConditionId = conditionId;
+ }
+
+ public @NonNull Builder setName(@NonNull String name) {
+ mName = name;
+ return this;
+ }
+
+ public @NonNull Builder setOwner(@Nullable ComponentName owner) {
+ mOwner = owner;
+ return this;
+ }
+
+ public @NonNull Builder setConditionId(@NonNull Uri conditionId) {
+ mConditionId = conditionId;
+ return this;
+ }
+
+ public @NonNull Builder setInterruptionFilter(
+ @InterruptionFilter int interruptionFilter) {
+ mInterruptionFilter = interruptionFilter;
+ return this;
+ }
+
+ public @NonNull Builder setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ return this;
+ }
+
+ public @NonNull Builder setConfigurationActivity(
+ @Nullable ComponentName configurationActivity) {
+ mConfigurationActivity = configurationActivity;
+ return this;
+ }
+
+ public @NonNull Builder setZenPolicy(@Nullable ZenPolicy policy) {
+ mPolicy = policy;
+ return this;
+ }
+
+ /**
+ * Sets the type of the rule
+ */
+ public @NonNull Builder setType(@Type int type) {
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Sets a user visible description of when this rule will be active
+ * (see {@link Condition#STATE_TRUE}).
+ *
+ * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
+ * "When connected to [Car Name]".
+ */
+ public @NonNull Builder setTriggerDescription(@Nullable String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Sets a resource id of a tintable vector drawable representing the rule in image form.
+ */
+ public @NonNull Builder setIconResId(@DrawableRes int iconResId) {
+ mIconResId = iconResId;
+ return this;
+ }
+
+ /**
+ * Sets whether this rule can be manually activated by the user even when the triggering
+ * condition for the rule is not met.
+ */
+ public @NonNull Builder setManualInvocationAllowed(boolean allowManualInvocation) {
+ mAllowManualInvocation = allowManualInvocation;
+ return this;
+ }
+
+ /**
+ * Sets the time at which this rule was created, in milliseconds since epoch
+ * @hide
+ */
+ public @NonNull Builder setCreationTime(long creationTime) {
+ mCreationTime = creationTime;
+ return this;
+ }
+
+ public @NonNull AutomaticZenRule build() {
+ AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity,
+ mConditionId, mPolicy, mInterruptionFilter, mEnabled);
+ rule.creationTime = mCreationTime;
+ rule.mType = mType;
+ rule.mTriggerDescription = mDescription;
+ rule.mIconResId = mIconResId;
+ rule.mAllowManualInvocation = mAllowManualInvocation;
+ rule.setPackageName(mPkg);
+
+ return rule;
+ }
+ }
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 520bf7d..260e985 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -949,4 +949,5 @@
* @param err The binder transaction error
*/
oneway void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err);
+ int getBindingUidProcessState(int uid, in String callingPackage);
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 1f8784b..df6badc 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -269,7 +269,10 @@
* task snapshot cache only if requested.
*
* @param taskId the id of the task to take a snapshot of
- * @param updateCache whether to store the new snapshot in the system's task snapshot cache
+ * @param updateCache Whether to store the new snapshot in the system's task snapshot cache.
+ * If it is true, the snapshot can be either real content or app-theme mode
+ * depending on the attributes of app. Otherwise, the snapshot will be taken
+ * with real content.
* @return a graphic buffer representing a screenshot of a task
*/
android.window.TaskSnapshot takeTaskSnapshot(int taskId, boolean updateCache);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ebf183f..f1e44cc 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -342,7 +342,9 @@
*/
public void updateApplicationInfo(@NonNull ApplicationInfo aInfo,
@Nullable List<String> oldPaths) {
- setApplicationInfo(aInfo);
+ if (!setApplicationInfo(aInfo)) {
+ return;
+ }
final List<String> newPaths = new ArrayList<>();
makePaths(mActivityThread, aInfo, newPaths);
@@ -387,7 +389,13 @@
mAppComponentFactory = createAppFactory(aInfo, mDefaultClassLoader);
}
- private void setApplicationInfo(ApplicationInfo aInfo) {
+ private boolean setApplicationInfo(ApplicationInfo aInfo) {
+ if (mApplicationInfo != null && mApplicationInfo.createTimestamp > aInfo.createTimestamp) {
+ Slog.w(TAG, "New application info for package " + aInfo.packageName
+ + " is out of date with TS " + aInfo.createTimestamp + " < the current TS "
+ + mApplicationInfo.createTimestamp);
+ return false;
+ }
final int myUid = Process.myUid();
aInfo = adjustNativeLibraryPaths(aInfo);
mApplicationInfo = aInfo;
@@ -410,6 +418,7 @@
if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
}
+ return true;
}
void setSdkSandboxStorage(@Nullable String sdkSandboxClientAppVolumeUuid,
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 2076e85..b303ea6 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -5,4 +5,11 @@
name: "app_start_info"
description: "Control collecting of ApplicationStartInfo records and APIs."
bug: "247814855"
-}
\ No newline at end of file
+}
+
+flag {
+ namespace: "backstage_power"
+ name: "get_binding_uid_importance"
+ description: "API to get importance of UID that's binding to the caller"
+ bug: "292533010"
+}
diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING
index 7809048..49a4467 100644
--- a/core/java/android/app/time/TEST_MAPPING
+++ b/core/java/android/app/time/TEST_MAPPING
@@ -1,24 +1,11 @@
{
- "presubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.app.time."
- }
- ]
- }
- ],
// TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
"postsubmit": [
{
- "name": "FrameworksCoreTests",
+ "name": "FrameworksTimeCoreTests",
"options": [
{
- "include-filter": "android.app.timedetector."
- },
- {
- "include-filter": "android.app.timezonedetector."
+ "include-filter": "android.app."
}
]
},
diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING
index 53fd74b..c050a55 100644
--- a/core/java/android/app/timedetector/TEST_MAPPING
+++ b/core/java/android/app/timedetector/TEST_MAPPING
@@ -2,13 +2,10 @@
// TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
"postsubmit": [
{
- "name": "FrameworksCoreTests",
+ "name": "FrameworksTimeCoreTests",
"options": [
{
- "include-filter": "android.app.time."
- },
- {
- "include-filter": "android.app.timedetector."
+ "include-filter": "android.app."
}
]
},
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
index 5e64c83..46656d1 100644
--- a/core/java/android/app/timezonedetector/TEST_MAPPING
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -1,21 +1,11 @@
{
- "presubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.app.timezonedetector."
- }
- ]
- }
- ],
// TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
"postsubmit": [
{
- "name": "FrameworksCoreTests",
+ "name": "FrameworksTimeCoreTests",
"options": [
{
- "include-filter": "android.app.time."
+ "include-filter": "android.app."
}
]
},
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 27f6a26..ec181da 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -156,6 +156,22 @@
}
/**
+ * @hide
+ */
+ public static class AdapterChildHostView extends AppWidgetHostView {
+
+ public AdapterChildHostView(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Context getRemoteContextEnsuringCorrectCachedApkPath() {
+ // To reduce noise in error messages
+ return null;
+ }
+ }
+
+ /**
* Set the AppWidget that will be displayed by this view. This method also adds default padding
* to widgets, as described in {@link #getDefaultPaddingForWidget(Context, ComponentName, Rect)}
* and can be overridden in order to add custom padding.
@@ -921,17 +937,31 @@
setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
}
+ private void setColorResourcesStates(RemoteViews.ColorResources colorResources) {
+ mColorResources = colorResources;
+ mColorMappingChanged = true;
+ mViewMode = VIEW_MODE_NOINIT;
+ }
+
/** @hide **/
public void setColorResources(RemoteViews.ColorResources colorResources) {
if (colorResources == mColorResources) {
return;
}
- mColorResources = colorResources;
- mColorMappingChanged = true;
- mViewMode = VIEW_MODE_NOINIT;
+ setColorResourcesStates(colorResources);
reapplyLastRemoteViews();
}
+ /**
+ * @hide
+ */
+ public void setColorResourcesNoReapply(RemoteViews.ColorResources colorResources) {
+ if (colorResources == mColorResources) {
+ return;
+ }
+ setColorResourcesStates(colorResources);
+ }
+
/** Check if, in the current context, the two color mappings are equivalent. */
private boolean isSameColorMapping(SparseIntArray oldColors, SparseIntArray newColors) {
if (oldColors.size() != newColors.size()) {
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index c2bc974..4b2cee6 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -235,12 +235,6 @@
}
/** @hide */
- public AttributionSource withUid(int uid) {
- return new AttributionSource(uid, getPid(), getPackageName(), getAttributionTag(),
- getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
- }
-
- /** @hide */
public AttributionSource withPid(int pid) {
return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 02e0cf6..ea54c91 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12587,12 +12587,8 @@
return (mFlags & FLAG_ACTIVITY_NEW_DOCUMENT) == FLAG_ACTIVITY_NEW_DOCUMENT;
}
- /**
- * @deprecated Use {@link SdkSandboxActivityAuthority#isSdkSandboxActivity} instead.
- * Once the other API is finalized this method will be removed.
- * @hide
- */
- @Deprecated
+ // TODO(b/299109198): Refactor into the {@link SdkSandboxManagerLocal}
+ /** @hide */
public boolean isSandboxActivity(@NonNull Context context) {
if (mAction != null && mAction.equals(ACTION_START_SANDBOXED_ACTIVITY)) {
return true;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e2a5747..72e1066 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6279,9 +6279,9 @@
* @hide
*/
@NonNull
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SystemApi
@TestApi
- @UnsupportedAppUsage
+ @SuppressLint("UnflaggedApi") // Promoting from @SystemApi(MODULE_LIBRARIES)
public String getPermissionControllerPackageName() {
throw new RuntimeException("Not implemented. Must override in a subclass.");
}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 43cf97f..9ec082a 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -35,3 +35,10 @@
description: "Further framework support for communal profile, beyond the basics, for later releases."
bug: "285426179"
}
+
+flag {
+ name: "use_all_cpus_during_user_switch"
+ namespace: "multiuser"
+ description: "Allow using all cpu cores during a user switch."
+ bug: "308105403"
+}
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 9b819a7..ec96215 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -13,3 +13,10 @@
description: "Enables Credential Manager to work with Instant Apps"
bug: "302190269"
}
+
+flag {
+ namespace: "credential_manager"
+ name: "clear_session_enabled"
+ description: "Enables clearing of Credential Manager sessions when client process dies"
+ bug: "308470501"
+}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 97bbfbb..8c1ea5f 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -857,7 +857,7 @@
* Get the operation handle associated with this object or 0 if none.
*/
@FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
- public long getOpId() {
+ public long getOperationHandle() {
return super.getOpId();
}
}
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 490b128..8f0149b 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -52,6 +52,11 @@
import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_BC_1_2;
import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP;
import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_OTHER;
+import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED;
+import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES;
+import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL;
+import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION;
+import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO;
import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE;
import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED;
@@ -789,6 +794,21 @@
case UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP:
complianceWarningString.append("missing rp, ");
break;
+ case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED:
+ complianceWarningString.append("input power limited, ");
+ break;
+ case UsbPortStatus.COMPLIANCE_WARNING_MISSING_DATA_LINES:
+ complianceWarningString.append("missing data lines, ");
+ break;
+ case UsbPortStatus.COMPLIANCE_WARNING_ENUMERATION_FAIL:
+ complianceWarningString.append("enumeration fail, ");
+ break;
+ case UsbPortStatus.COMPLIANCE_WARNING_FLAKY_CONNECTION:
+ complianceWarningString.append("flaky connection, ");
+ break;
+ case UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO:
+ complianceWarningString.append("unreliable io, ");
+ break;
default:
complianceWarningString.append(String.format("Unknown(%d), ", warning));
break;
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index b4fe3a2..d959240 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -18,11 +18,13 @@
import android.Manifest;
import android.annotation.CheckResult;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.hardware.usb.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
@@ -310,6 +312,54 @@
public static final int COMPLIANCE_WARNING_MISSING_RP = 4;
/**
+ * Used to indicate the charging setups on the USB ports are unable to
+ * deliver negotiated power. Introduced in Android V (API level 35)
+ * and client applicantions that target API levels lower than 35 will
+ * receive {@link #COMPLIANCE_WARNING_OTHER} instead.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING)
+ public static final int COMPLIANCE_WARNING_INPUT_POWER_LIMITED = 5;
+
+ /**
+ * Used to indicate the cable/connector on the USB ports are missing
+ * the required wires on the data pins to make data transfer.
+ * Introduced in Android V (API level 35) and client applicantions that
+ * target API levels lower than 35 will receive
+ * {@link #COMPLIANCE_WARNING_OTHER} instead.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING)
+ public static final int COMPLIANCE_WARNING_MISSING_DATA_LINES = 6;
+
+ /**
+ * Used to indicate enumeration failures on the USB ports, potentially due to
+ * signal integrity issues or other causes. Introduced in Android V
+ * (API level 35) and client applicantions that target API levels lower
+ * than 35 will receive {@link #COMPLIANCE_WARNING_OTHER} instead.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING)
+ public static final int COMPLIANCE_WARNING_ENUMERATION_FAIL = 7;
+
+ /**
+ * Used to indicate unexpected data disconnection on the USB ports,
+ * potentially due to signal integrity issues or other causes.
+ * Introduced in Android V (API level 35) and client applicantions that
+ * target API levels lower than 35 will receive
+ * {@link #COMPLIANCE_WARNING_OTHER} instead.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING)
+ public static final int COMPLIANCE_WARNING_FLAKY_CONNECTION = 8;
+
+ /**
+ * Used to indicate unreliable or slow data transfer on the USB ports,
+ * potentially due to signal integrity issues or other causes.
+ * Introduced in Android V (API level 35) and client applicantions that
+ * target API levels lower than 35 will receive
+ * {@link #COMPLIANCE_WARNING_OTHER} instead.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING)
+ public static final int COMPLIANCE_WARNING_UNRELIABLE_IO = 9;
+
+ /**
* Indicates that the Type-C plug orientation cannot be
* determined because the connected state of the device is unknown.
*/
@@ -372,6 +422,11 @@
COMPLIANCE_WARNING_DEBUG_ACCESSORY,
COMPLIANCE_WARNING_BC_1_2,
COMPLIANCE_WARNING_MISSING_RP,
+ COMPLIANCE_WARNING_INPUT_POWER_LIMITED,
+ COMPLIANCE_WARNING_MISSING_DATA_LINES,
+ COMPLIANCE_WARNING_ENUMERATION_FAIL,
+ COMPLIANCE_WARNING_FLAKY_CONNECTION,
+ COMPLIANCE_WARNING_UNRELIABLE_IO,
})
@Retention(RetentionPolicy.SOURCE)
@interface ComplianceWarning{}
@@ -591,7 +646,12 @@
* @return array including {@link #COMPLIANCE_WARNING_OTHER},
* {@link #COMPLIANCE_WARNING_DEBUG_ACCESSORY},
* {@link #COMPLIANCE_WARNING_BC_1_2},
- * or {@link #COMPLIANCE_WARNING_MISSING_RP}
+ * {@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}.
*/
@CheckResult
@NonNull
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
new file mode 100644
index 0000000..5c5083a
--- /dev/null
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -0,0 +1,15 @@
+package: "android.hardware.usb.flags"
+
+flag {
+ name: "enable_usb_data_compliance_warning"
+ namespace: "system_sw_usb"
+ description: "Enable USB data compliance warnings when set"
+ bug: "296119135"
+}
+
+flag {
+ name: "enable_input_power_limited_warning"
+ namespace: "system_sw_usb"
+ description: "Flag incompatible charging on COMPLIANCE_WARNING_INPUT_POWER_LIMITED instead of COMPLIANCE_WARNING_OTHER when enabled"
+ bug: "308700954"
+}
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index dbb3127..19ba6a1 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -23,6 +23,7 @@
import android.os.IBinder;
import android.os.ServiceManager;
+import com.android.net.flags.Flags;
import com.android.net.module.util.PermissionUtils;
/**
* Constants and utilities for client code communicating with the network stack service.
@@ -103,4 +104,16 @@
final @NonNull String... otherPermissions) {
PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
}
+
+ /**
+ * Get setting of the "set_data_saver_via_cm" flag.
+ *
+ * @hide
+ */
+ // A workaround for aconfig. Currently, aconfig value read from platform and mainline code can
+ // be inconsistent. To avoid the problem, CTS for mainline code can get the flag value by this
+ // method.
+ public static boolean getDataSaverViaCmFlag() {
+ return Flags.setDataSaverViaCm();
+ }
}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 9cf8c4d..597c948 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -129,7 +129,7 @@
/**
* State of the service for CATEGORY_OTHER selection
*/
- private boolean mOtherServiceSelectionState;
+ private boolean mOtherServiceEnabled;
/**
* @hide
@@ -150,10 +150,10 @@
List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost,
- boolean isSelected) {
+ boolean isEnabled) {
this(info, onHost, description, staticAidGroups, dynamicAidGroups,
requiresUnlock, onHost ? true : false, bannerResource, uid,
- settingsActivityName, offHost, staticOffHost, isSelected);
+ settingsActivityName, offHost, staticOffHost, isEnabled);
}
/**
@@ -162,7 +162,7 @@
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
- String settingsActivityName, String offHost, String staticOffHost, boolean isSelected) {
+ String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -181,7 +181,7 @@
this.mBannerResourceId = bannerResource;
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
- this.mOtherServiceSelectionState = isSelected;
+ this.mOtherServiceEnabled = isEnabled;
}
@@ -372,7 +372,7 @@
// Set uid
mUid = si.applicationInfo.uid;
- mOtherServiceSelectionState = false; // support other category
+ mOtherServiceEnabled = false; // support other category
}
@@ -744,7 +744,7 @@
dest.writeInt(mUid);
dest.writeString(mSettingsActivityName);
- dest.writeInt(mOtherServiceSelectionState ? 1 : 0);
+ dest.writeInt(mOtherServiceEnabled ? 1 : 0);
};
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -772,11 +772,11 @@
int bannerResource = source.readInt();
int uid = source.readInt();
String settingsActivityName = source.readString();
- boolean isSelected = source.readInt() != 0;
+ boolean isEnabled = source.readInt() != 0;
return new ApduServiceInfo(info, onHost, description, staticAidGroups,
dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
settingsActivityName, offHostName, staticOffHostName,
- isSelected);
+ isEnabled);
}
@Override
@@ -807,7 +807,7 @@
pw.println(" Static AID groups:");
for (AidGroup group : mStaticAidGroups.values()) {
pw.println(" Category: " + group.getCategory()
- + "(selected: " + mOtherServiceSelectionState + ")");
+ + "(enabled: " + mOtherServiceEnabled + ")");
for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
@@ -815,7 +815,7 @@
pw.println(" Dynamic AID groups:");
for (AidGroup group : mDynamicAidGroups.values()) {
pw.println(" Category: " + group.getCategory()
- + "(selected: " + mOtherServiceSelectionState + ")");
+ + "(enabled: " + mOtherServiceEnabled + ")");
for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
@@ -827,18 +827,24 @@
/**
- * @hide
+ * Enable or disable this CATEGORY_OTHER service.
+ *
+ * @param enabled true to indicate if user has enabled this service
*/
- public void setOtherServiceState(boolean selected) {
- mOtherServiceSelectionState = selected;
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setOtherServiceEnabled(boolean enabled) {
+ mOtherServiceEnabled = enabled;
}
/**
- * @hide
+ * Returns whether this CATEGORY_OTHER service is enabled or not.
+ *
+ * @return true to indicate if user has enabled this service
*/
- public boolean isSelectedOtherService() {
- return mOtherServiceSelectionState;
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public boolean isOtherServiceEnabled() {
+ return mOtherServiceEnabled;
}
/**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 3db1cb0..9956220 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -333,6 +333,12 @@
@CriticalNative
public static final native boolean isDirectlyHandlingTransactionNative();
+ /** @hide */
+ public static final boolean isDirectlyHandlingTransactionNative$ravenwood() {
+ // Ravenwood doesn't support IPC
+ return false;
+ }
+
private static boolean sIsHandlingBinderTransaction = false;
/**
@@ -715,7 +721,9 @@
*/
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
- NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
+ if (mObject != 0L) {
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
+ }
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
@@ -1277,6 +1285,10 @@
private static native long getNativeBBinderHolder();
+ private static long getNativeBBinderHolder$ravenwood() {
+ return 0L;
+ }
+
/**
* By default, we use the calling UID since we can always trust it.
*/
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 352c9d2..86628d9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1558,7 +1558,7 @@
ensureWithinMemoryLimit(typeSize, totalObjects);
}
- private void ensureWithinMemoryLimit(int typeSize, @NonNull int length) {
+ private void ensureWithinMemoryLimit(int typeSize, int length) {
int estimatedAllocationSize = 0;
try {
estimatedAllocationSize = Math.multiplyExact(typeSize, length);
@@ -2957,6 +2957,14 @@
}
/** @hide */
+ public final void writeException$ravenwood(@NonNull Exception e) {
+ // Ravenwood doesn't support IPC, no transaction headers needed
+ writeInt(getExceptionCode(e));
+ writeString(e.getMessage());
+ writeInt(0);
+ }
+
+ /** @hide */
public static int getExceptionCode(@NonNull Throwable e) {
int code = 0;
if (e instanceof Parcelable
@@ -3039,6 +3047,12 @@
}
}
+ /** @hide */
+ public final void writeNoException$ravenwood() {
+ // Ravenwood doesn't support IPC, no transaction headers needed
+ writeInt(0);
+ }
+
/**
* Special function for reading an exception result from the header of
* a parcel, to be used after receiving the result of a transaction. This
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 47b6d8d..94f90cc 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2018,9 +2018,13 @@
return;
}
+ // Temporarily disable checks so that explicit GC is allowed.
+ final int oldMask = getThreadPolicyMask();
+ setThreadPolicyMask(0);
System.gc();
System.runFinalization();
System.gc();
+ setThreadPolicyMask(oldMask);
// Note: classInstanceLimit is immutable, so this is lock-free
// Create the classes array.
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 0133bd8..9bdd0c2 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -36,3 +36,10 @@
description: "Collect sepolicy hash from sysfs"
bug: "308471499"
}
+
+flag {
+ name: "extend_ecm_to_all_settings"
+ namespace: "responsible_apis"
+ description: "Allow all app settings to be restrictable via configuration"
+ bug: "297372999"
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 828c062..ff4dfc7 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -28,6 +28,8 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.AutomaticZenRule;
+import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.compat.annotation.UnsupportedAppUsage;
@@ -177,6 +179,10 @@
private static final String RULE_ATT_CREATION_TIME = "creationTime";
private static final String RULE_ATT_ENABLER = "enabler";
private static final String RULE_ATT_MODIFIED = "modified";
+ private static final String RULE_ATT_ALLOW_MANUAL = "userInvokable";
+ private static final String RULE_ATT_TYPE = "type";
+ private static final String RULE_ATT_ICON = "rule_icon";
+ private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
@UnsupportedAppUsage
public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
@@ -212,7 +218,7 @@
allowCallsFrom = source.readInt();
allowMessagesFrom = source.readInt();
user = source.readInt();
- manualRule = source.readParcelable(null, android.service.notification.ZenModeConfig.ZenRule.class);
+ manualRule = source.readParcelable(null, ZenRule.class);
final int len = source.readInt();
if (len > 0) {
final String[] ids = new String[len];
@@ -622,6 +628,12 @@
}
rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
rt.zenPolicy = readZenPolicyXml(parser);
+ if (Flags.modesApi()) {
+ rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false);
+ rt.iconResId = safeInt(parser, RULE_ATT_ICON, 0);
+ rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC);
+ rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN);
+ }
return rt;
}
@@ -655,6 +667,14 @@
writeZenPolicyXml(rule.zenPolicy, out);
}
out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified);
+ if (Flags.modesApi()) {
+ out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation);
+ out.attributeInt(null, RULE_ATT_ICON, rule.iconResId);
+ if (rule.triggerDescription != null) {
+ out.attribute(null, RULE_ATT_TRIGGER_DESC, rule.triggerDescription);
+ }
+ out.attributeInt(null, RULE_ATT_TYPE, rule.type);
+ }
}
public static Condition readConditionXml(TypedXmlPullParser parser) {
@@ -1726,6 +1746,11 @@
public ZenPolicy zenPolicy;
public boolean modified; // rule has been modified from initial creation
public String pkg;
+ public int type = AutomaticZenRule.TYPE_UNKNOWN;
+ public String triggerDescription;
+ // TODO (b/308672670): switch to string res name
+ public int iconResId;
+ public boolean allowManualInvocation;
public ZenRule() { }
@@ -1750,6 +1775,12 @@
zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
modified = source.readInt() == 1;
pkg = source.readString();
+ if (Flags.modesApi()) {
+ allowManualInvocation = source.readBoolean();
+ iconResId = source.readInt();
+ triggerDescription = source.readString();
+ type = source.readInt();
+ }
}
@Override
@@ -1788,11 +1819,17 @@
dest.writeParcelable(zenPolicy, 0);
dest.writeInt(modified ? 1 : 0);
dest.writeString(pkg);
+ if (Flags.modesApi()) {
+ dest.writeBoolean(allowManualInvocation);
+ dest.writeInt(iconResId);
+ dest.writeString(triggerDescription);
+ dest.writeInt(type);
+ }
}
@Override
public String toString() {
- return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
+ StringBuilder sb = new StringBuilder(ZenRule.class.getSimpleName()).append('[')
.append("id=").append(id)
.append(",state=").append(condition == null ? "STATE_FALSE"
: Condition.stateToString(condition.state))
@@ -1808,8 +1845,16 @@
.append(",enabler=").append(enabler)
.append(",zenPolicy=").append(zenPolicy)
.append(",modified=").append(modified)
- .append(",condition=").append(condition)
- .append(']').toString();
+ .append(",condition=").append(condition);
+
+ if (Flags.modesApi()) {
+ sb.append(",allowManualInvocation=").append(allowManualInvocation)
+ .append(",iconResId=").append(iconResId)
+ .append(",triggerDescription=").append(triggerDescription)
+ .append(",type=").append(type);
+ }
+
+ return sb.append(']').toString();
}
/** @hide */
@@ -1845,7 +1890,7 @@
if (!(o instanceof ZenRule)) return false;
if (o == this) return true;
final ZenRule other = (ZenRule) o;
- return other.enabled == enabled
+ boolean finalEquals = other.enabled == enabled
&& other.snoozing == snoozing
&& Objects.equals(other.name, name)
&& other.zenMode == zenMode
@@ -1858,10 +1903,25 @@
&& Objects.equals(other.zenPolicy, zenPolicy)
&& Objects.equals(other.pkg, pkg)
&& other.modified == modified;
+
+ if (Flags.modesApi()) {
+ return finalEquals
+ && other.allowManualInvocation == allowManualInvocation
+ && other.iconResId == iconResId
+ && Objects.equals(other.triggerDescription, triggerDescription)
+ && other.type == type;
+ }
+
+ return finalEquals;
}
@Override
public int hashCode() {
+ if (Flags.modesApi()) {
+ return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
+ component, configurationActivity, pkg, id, enabler, zenPolicy, modified,
+ allowManualInvocation, iconResId, triggerDescription, type);
+ }
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
}
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index a4f129e..eb55e40 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -454,6 +454,11 @@
public static final String FIELD_ZEN_POLICY = "zenPolicy";
public static final String FIELD_MODIFIED = "modified";
public static final String FIELD_PKG = "pkg";
+ public static final String FIELD_ALLOW_MANUAL = "allowManualInvocation";
+ public static final String FIELD_ICON_RES = "iconResId";
+ public static final String FIELD_TRIGGER = "triggerDescription";
+ public static final String FIELD_TYPE = "type";
+ // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule
// Special field to track whether this rule became active or inactive
FieldDiff<Boolean> mActiveDiff;
@@ -529,6 +534,20 @@
if (!Objects.equals(from.pkg, to.pkg)) {
addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg));
}
+ if (!Objects.equals(from.triggerDescription, to.triggerDescription)) {
+ addField(FIELD_TRIGGER,
+ new FieldDiff<>(from.triggerDescription, to.triggerDescription));
+ }
+ if (from.type != to.type) {
+ addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type));
+ }
+ if (from.allowManualInvocation != to.allowManualInvocation) {
+ addField(FIELD_ALLOW_MANUAL,
+ new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation));
+ }
+ if (!Objects.equals(from.iconResId, to.iconResId)) {
+ addField(FIELD_ICON_RES, new FieldDiff(from.iconResId, to.iconResId));
+ }
}
/**
diff --git a/core/java/android/service/timezone/TEST_MAPPING b/core/java/android/service/timezone/TEST_MAPPING
index b0ce1db..21a8eab 100644
--- a/core/java/android/service/timezone/TEST_MAPPING
+++ b/core/java/android/service/timezone/TEST_MAPPING
@@ -2,10 +2,10 @@
// TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
"postsubmit": [
{
- "name": "FrameworksCoreTests",
+ "name": "FrameworksTimeCoreTests",
"options": [
{
- "include-filter": "android.service.timezone."
+ "include-filter": "android.service."
}
]
},
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 0d5704e..1acc384 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -18,6 +18,7 @@
import static android.view.InsetsSourceProto.FRAME;
import static android.view.InsetsSourceProto.TYPE;
+import static android.view.InsetsSourceProto.TYPE_NUMBER;
import static android.view.InsetsSourceProto.VISIBLE;
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
import static android.view.WindowInsets.Type.captionBar;
@@ -352,6 +353,7 @@
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(TYPE, WindowInsets.Type.toString(mType));
+ proto.write(TYPE_NUMBER, mType);
mFrame.dumpDebug(proto, FRAME);
if (mVisibleFrame != null) {
mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c9c1f20..d97dfb0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -123,6 +123,7 @@
import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ForceDarkType;
import android.graphics.FrameInfo;
import android.graphics.HardwareRenderer;
import android.graphics.HardwareRenderer.FrameDrawingCallback;
@@ -1796,7 +1797,7 @@
/** Returns true if force dark should be enabled according to various settings */
@VisibleForTesting
- public boolean isForceDarkEnabled() {
+ public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
if (forceInvertColor()) {
boolean isForceInvertEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
@@ -1808,7 +1809,7 @@
// for dark mode in configuration.uiMode. Instead, we assume that the force invert
// setting will be enabled at the same time dark theme is in the Settings app.
if (isForceInvertEnabled) {
- return true;
+ return ForceDarkType.FORCE_INVERT_COLOR_DARK;
}
}
@@ -1822,12 +1823,12 @@
&& a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault);
a.recycle();
}
- return useAutoDark;
+ return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE;
}
private void updateForceDarkMode() {
if (mAttachInfo.mThreadedRenderer == null) return;
- if (mAttachInfo.mThreadedRenderer.setForceDark(isForceDarkEnabled())) {
+ if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
// TODO: Don't require regenerating all display lists to apply this setting
invalidateWorld(mView);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cfec081..c735142 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -17,7 +17,6 @@
package android.view;
import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
-import static android.view.flags.Flags.FLAG_WM_DISPLAY_REFRESH_RATE_TEST;
import static android.view.View.STATUS_BAR_DISABLE_BACK;
import static android.view.View.STATUS_BAR_DISABLE_CLOCK;
import static android.view.View.STATUS_BAR_DISABLE_EXPAND;
@@ -3907,7 +3906,7 @@
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
- @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
@TestApi
public float preferredMinDisplayRefreshRate;
@@ -3917,7 +3916,7 @@
* This value is ignored if {@link #preferredDisplayModeId} is set.
* @hide
*/
- @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
@TestApi
public float preferredMaxDisplayRefreshRate;
diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index cc951cf..a467afe 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -42,11 +42,4 @@
namespace: "core_graphics"
description: "Enable the `setFrameRate` callback"
bug: "299946220"
-}
-
-flag {
- name: "wm_display_refresh_rate_test"
- namespace: "core_graphics"
- description: "Adds WindowManager display refresh rate fields to test API"
- bug: "304475199"
}
\ No newline at end of file
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 7f0a651..e20357fa 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -12,6 +12,6 @@
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
-per-file RemoteViews* = file:../appwidget/OWNERS
+per-file Remote* = file:../appwidget/OWNERS
per-file Toast.java = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
index d843308..9b396ae 100644
--- a/core/java/android/widget/RemoteCollectionItemsAdapter.java
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.appwidget.AppWidgetHostView;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
@@ -25,8 +26,6 @@
import android.widget.RemoteViews.InteractionHandler;
import android.widget.RemoteViews.RemoteCollectionItems;
-import com.android.internal.R;
-
import java.util.stream.IntStream;
/**
@@ -178,40 +177,14 @@
RemoteViews item = mItems.getItemView(position);
item.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
- View reapplyView = getViewToReapply(convertView, item);
- // Reapply the RemoteViews if we can.
- if (reapplyView != null) {
- try {
- item.reapply(
- parent.getContext(),
- reapplyView,
- mInteractionHandler,
- null /* size */,
- mColorResources);
- return reapplyView;
- } catch (RuntimeException e) {
- // We can't reapply for some reason, we'll fallback to an apply and inflate a
- // new view.
- }
- }
-
- return item.apply(
- parent.getContext(),
- parent,
- mInteractionHandler,
- null /* size */,
- mColorResources);
- }
-
- /** Returns {@code convertView} if it can be used to reapply {@code item}, or null otherwise. */
- @Nullable
- private static View getViewToReapply(@Nullable View convertView, @NonNull RemoteViews item) {
- if (convertView == null) return null;
-
- Object layoutIdTag = convertView.getTag(R.id.widget_frame);
- if (!(layoutIdTag instanceof Integer)) return null;
-
- return item.getLayoutId() == (Integer) layoutIdTag ? convertView : null;
+ AppWidgetHostView newView = convertView instanceof AppWidgetHostView.AdapterChildHostView
+ widgetChildView
+ ? widgetChildView
+ : new AppWidgetHostView.AdapterChildHostView(parent.getContext());
+ newView.setInteractionHandler(mInteractionHandler);
+ newView.setColorResourcesNoReapply(mColorResources);
+ newView.updateAppWidget(item);
+ return newView;
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 403b403..a919c00 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6958,13 +6958,13 @@
View parent = (View) view.getParent();
// Break the for loop on the first encounter of:
// 1) an AdapterView,
- // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
+ // 2) an AppWidgetHostView that is not a child of an adapter view, or
// 3) a null parent.
// 2) and 3) are unexpected and catch the case where a child is not
// correctly parented in an AdapterView.
while (parent != null && !(parent instanceof AdapterView<?>)
&& !((parent instanceof AppWidgetHostView)
- && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
+ && !(parent instanceof AppWidgetHostView.AdapterChildHostView))) {
parent = (View) parent.getParent();
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 61a7599..2f28a87 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -368,7 +368,7 @@
* A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
* they are loaded.
*/
- static class RemoteViewsFrameLayout extends AppWidgetHostView {
+ static class RemoteViewsFrameLayout extends AppWidgetHostView.AdapterChildHostView {
private final FixedSizeRemoteViewsCache mCache;
public int cacheIndex = -1;
@@ -408,11 +408,6 @@
}
@Override
- protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
- return null;
- }
-
- @Override
protected View getErrorView() {
// Use the default loading view as the error view.
return getDefaultView();
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 4bfb177..94e6009 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -19,4 +19,11 @@
namespace: "responsible_apis"
description: "Enable toasts to indicate (potential) BAL blocking."
bug: "308059069"
+}
+
+flag {
+ name: "bal_show_toasts_blocked"
+ namespace: "responsible_apis"
+ description: "Enable toasts to indicate actual BAL blocking."
+ bug: "308059069"
}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 24dc6db..0ad6c99 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -29,3 +29,10 @@
description: "Whether to dispatch window resize through ClientTransaction is enabled"
bug: "301870955"
}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "fullscreen_dim_flag"
+ description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
+ bug: "253533308"
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 1b05982..492e2ac 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -42,22 +42,18 @@
// frameworks/native/libs/permission/include/binder/IAppOpsService.h must match the order here.
// Please be careful to respect both these issues when modifying this file.
interface IAppOpsService {
- // Deprecated, use checkOperationWithState instead.
+ // These methods are also called by native code, so please be careful that the number in
+ // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
int checkOperation(int code, int uid, String packageName);
- // Deprecated, use noteOperationWithState instead.
SyncNotedAppOp noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
- // Deprecated, use startOperationWithState instead.
SyncNotedAppOp startOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, boolean startIfModeDefault,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
int attributionFlags, int attributionChainId);
- // Deprecated, use finishOperationWithState instead.
@UnsupportedAppUsage
void finishOperation(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag);
- // These methods are also called by native code, so please be careful that the number in
- // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
void stopWatchingMode(IAppOpsCallback callback);
int permissionToOpCode(String permission);
@@ -138,33 +134,20 @@
void stopWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback);
List<AsyncNotedAppOp> extractAsyncOps(String packageName);
- // Deprecated, use checkOperationWithStateRaw instead.
int checkOperationRaw(int code, int uid, String packageName, @nullable String attributionTag);
void reloadNonHistoricalState();
void collectNoteOpCallsForValidation(String stackTrace, int op, String packageName, long version);
- // These methods are also called by native code, so please be careful that the number in
- // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
- int checkOperationWithState(int code, in AttributionSourceState attributionSourceState);
- int checkOperationWithStateRaw(int code, in AttributionSourceState attributionSourceState);
- SyncNotedAppOp noteOperationWithState(int code, in AttributionSourceState attributionSourceState,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
- SyncNotedAppOp startOperationWithState(IBinder clientId, int code,
- in AttributionSourceState attributionSourceState, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- int attributionFlags, int attributionChainId);
- void finishOperationWithState(IBinder clientId, int code, in AttributionSourceState attributionSourceState);
- // End of methods also called by native code (there may be more blocks like this of native
- // methods later in this file).
+
SyncNotedAppOp noteProxyOperationWithState(int code,
- in AttributionSourceState attributionSourceStateState,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation);
+ in AttributionSourceState attributionSourceStateState,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation);
SyncNotedAppOp startProxyOperationWithState(IBinder clientId, int code,
- in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
- int attributionChainId);
+ in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
+ boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+ boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
+ int attributionChainId);
void finishProxyOperationWithState(IBinder clientId, int code,
- in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
+ in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
}
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 08de4dfb..be3f10a 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -287,7 +287,6 @@
updatedView = mInflater.inflate(
R.layout.app_language_picker_current_locale_item,
parent, false);
- addStateDescriptionIntoCurrentLocaleItem(updatedView);
}
} else {
shouldReuseView = convertView instanceof TextView
@@ -304,7 +303,6 @@
if (!shouldReuseView) {
updatedView = mInflater.inflate(
R.layout.app_language_picker_current_locale_item, parent, false);
- addStateDescriptionIntoCurrentLocaleItem(updatedView);
}
break;
default:
@@ -441,9 +439,4 @@
: View.TEXT_DIRECTION_LTR);
}
}
-
- private void addStateDescriptionIntoCurrentLocaleItem(View root) {
- String description = root.getContext().getResources().getString(R.string.checked);
- root.setStateDescription(description);
- }
}
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 9d88a23..d85227f 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -70,9 +70,6 @@
mPackageFilt = new IntentFilter();
// Settings app sends the broadcast
mPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- // AMS sends the broadcast
- mPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- mPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
mPackageFilt.addDataScheme("package");
if (isCore) {
mPackageFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e014ab0..6c17e9e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -65,6 +65,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
import android.transition.Scene;
@@ -183,6 +184,12 @@
private static final Transition USE_DEFAULT_TRANSITION = new TransitionSet();
/**
+ * Since which target SDK version this window should be edge-to-edge by default.
+ */
+ private static final int DEFAULT_EDGE_TO_EDGE_SDK_VERSION =
+ SystemProperties.getInt("persist.wm.debug.default_e2e_since_sdk", Integer.MAX_VALUE);
+
+ /**
* Simple callback used by the context menu and its submenus. The options
* menu submenus do not use this (their behavior is more complex).
*/
@@ -359,6 +366,8 @@
boolean mDecorFitsSystemWindows = true;
+ private final boolean mDefaultEdgeToEdge;
+
private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher;
static class WindowManagerHolder {
@@ -377,6 +386,11 @@
mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher(context);
mAllowFloatingWindowsFillScreen = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowFloatingWindowsFillScreen);
+ mDefaultEdgeToEdge =
+ context.getApplicationInfo().targetSdkVersion >= DEFAULT_EDGE_TO_EDGE_SDK_VERSION;
+ if (mDefaultEdgeToEdge) {
+ mDecorFitsSystemWindows = false;
+ }
}
/**
@@ -2527,7 +2541,14 @@
final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q;
if (!mForcedStatusBarColor) {
- mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, Color.BLACK);
+ final int statusBarCompatibleColor = context.getColor(R.color.status_bar_compatible);
+ final int statusBarDefaultColor = context.getColor(R.color.status_bar_default);
+ final int statusBarColor = a.getColor(R.styleable.Window_statusBarColor,
+ statusBarDefaultColor);
+
+ mStatusBarColor = statusBarColor == statusBarDefaultColor && !mDefaultEdgeToEdge
+ ? statusBarCompatibleColor
+ : statusBarColor;
}
if (!mForcedNavigationBarColor) {
final int navBarCompatibleColor = context.getColor(R.color.navigation_bar_compatible);
@@ -2541,6 +2562,7 @@
&& Flags.navBarTransparentByDefault())
&& !context.getResources().getBoolean(
R.bool.config_navBarDefaultTransparent)
+ && !mDefaultEdgeToEdge
? navBarCompatibleColor
: navBarColor;
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
deleted file mode 100644
index 929c9e8..0000000
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2006 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.util;
-
-import android.compat.annotation.UnsupportedAppUsage;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.UnsupportedCharsetException;
-
-/**
- * This is a quick and dirty implementation of XmlSerializer that isn't horribly
- * painfully slow like the normal one. It only does what is needed for the
- * specific XML files being written with it.
- */
-public class FastXmlSerializer implements XmlSerializer {
- private static final String ESCAPE_TABLE[] = new String[] {
- "�", "", "", "", "", "", "", "", // 0-7
- "", "	", " ", "", "", " ", "", "", // 8-15
- "", "", "", "", "", "", "", "", // 16-23
- "", "", "", "", "", "", "", "", // 24-31
- null, null, """, null, null, null, "&", null, // 32-39
- null, null, null, null, null, null, null, null, // 40-47
- null, null, null, null, null, null, null, null, // 48-55
- null, null, null, null, "<", null, ">", null, // 56-63
- };
-
- private static final int DEFAULT_BUFFER_LEN = 32*1024;
-
- private static String sSpace = " ";
-
- private final int mBufferLen;
- private final char[] mText;
- private int mPos;
-
- private Writer mWriter;
-
- private OutputStream mOutputStream;
- private CharsetEncoder mCharset;
- private ByteBuffer mBytes;
-
- private boolean mIndent = false;
- private boolean mInTag;
-
- private int mNesting = 0;
- private boolean mLineStart = true;
-
- @UnsupportedAppUsage
- public FastXmlSerializer() {
- this(DEFAULT_BUFFER_LEN);
- }
-
- /**
- * Allocate a FastXmlSerializer with the given internal output buffer size. If the
- * size is zero or negative, then the default buffer size will be used.
- *
- * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use.
- */
- public FastXmlSerializer(int bufferSize) {
- mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN;
- mText = new char[mBufferLen];
- mBytes = ByteBuffer.allocate(mBufferLen);
- }
-
- private void append(char c) throws IOException {
- int pos = mPos;
- if (pos >= (mBufferLen-1)) {
- flush();
- pos = mPos;
- }
- mText[pos] = c;
- mPos = pos+1;
- }
-
- private void append(String str, int i, final int length) throws IOException {
- if (length > mBufferLen) {
- final int end = i + length;
- while (i < end) {
- int next = i + mBufferLen;
- append(str, i, next<end ? mBufferLen : (end-i));
- i = next;
- }
- return;
- }
- int pos = mPos;
- if ((pos+length) > mBufferLen) {
- flush();
- pos = mPos;
- }
- str.getChars(i, i+length, mText, pos);
- mPos = pos + length;
- }
-
- private void append(char[] buf, int i, final int length) throws IOException {
- if (length > mBufferLen) {
- final int end = i + length;
- while (i < end) {
- int next = i + mBufferLen;
- append(buf, i, next<end ? mBufferLen : (end-i));
- i = next;
- }
- return;
- }
- int pos = mPos;
- if ((pos+length) > mBufferLen) {
- flush();
- pos = mPos;
- }
- System.arraycopy(buf, i, mText, pos, length);
- mPos = pos + length;
- }
-
- private void append(String str) throws IOException {
- append(str, 0, str.length());
- }
-
- private void appendIndent(int indent) throws IOException {
- indent *= 4;
- if (indent > sSpace.length()) {
- indent = sSpace.length();
- }
- append(sSpace, 0, indent);
- }
-
- private void escapeAndAppendString(final String string) throws IOException {
- final int N = string.length();
- final char NE = (char)ESCAPE_TABLE.length;
- final String[] escapes = ESCAPE_TABLE;
- int lastPos = 0;
- int pos;
- for (pos=0; pos<N; pos++) {
- char c = string.charAt(pos);
- if (c >= NE) continue;
- String escape = escapes[c];
- if (escape == null) continue;
- if (lastPos < pos) append(string, lastPos, pos-lastPos);
- lastPos = pos + 1;
- append(escape);
- }
- if (lastPos < pos) append(string, lastPos, pos-lastPos);
- }
-
- private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {
- final char NE = (char)ESCAPE_TABLE.length;
- final String[] escapes = ESCAPE_TABLE;
- int end = start+len;
- int lastPos = start;
- int pos;
- for (pos=start; pos<end; pos++) {
- char c = buf[pos];
- if (c >= NE) continue;
- String escape = escapes[c];
- if (escape == null) continue;
- if (lastPos < pos) append(buf, lastPos, pos-lastPos);
- lastPos = pos + 1;
- append(escape);
- }
- if (lastPos < pos) append(buf, lastPos, pos-lastPos);
- }
-
- public XmlSerializer attribute(String namespace, String name, String value) throws IOException,
- IllegalArgumentException, IllegalStateException {
- append(' ');
- if (namespace != null) {
- append(namespace);
- append(':');
- }
- append(name);
- append("=\"");
-
- escapeAndAppendString(value);
- append('"');
- mLineStart = false;
- return this;
- }
-
- public void cdsect(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void comment(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void docdecl(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
- flush();
- }
-
- public XmlSerializer endTag(String namespace, String name) throws IOException,
- IllegalArgumentException, IllegalStateException {
- mNesting--;
- if (mInTag) {
- append(" />\n");
- } else {
- if (mIndent && mLineStart) {
- appendIndent(mNesting);
- }
- append("</");
- if (namespace != null) {
- append(namespace);
- append(':');
- }
- append(name);
- append(">\n");
- }
- mLineStart = true;
- mInTag = false;
- return this;
- }
-
- public void entityRef(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- private void flushBytes() throws IOException {
- int position;
- if ((position = mBytes.position()) > 0) {
- mBytes.flip();
- mOutputStream.write(mBytes.array(), 0, position);
- mBytes.clear();
- }
- }
-
- public void flush() throws IOException {
- //Log.i("PackageManager", "flush mPos=" + mPos);
- if (mPos > 0) {
- if (mOutputStream != null) {
- CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
- CoderResult result = mCharset.encode(charBuffer, mBytes, true);
- while (true) {
- if (result.isError()) {
- throw new IOException(result.toString());
- } else if (result.isOverflow()) {
- flushBytes();
- result = mCharset.encode(charBuffer, mBytes, true);
- continue;
- }
- break;
- }
- flushBytes();
- mOutputStream.flush();
- } else {
- mWriter.write(mText, 0, mPos);
- mWriter.flush();
- }
- mPos = 0;
- }
- }
-
- public int getDepth() {
- throw new UnsupportedOperationException();
- }
-
- public boolean getFeature(String name) {
- throw new UnsupportedOperationException();
- }
-
- public String getName() {
- throw new UnsupportedOperationException();
- }
-
- public String getNamespace() {
- throw new UnsupportedOperationException();
- }
-
- public String getPrefix(String namespace, boolean generatePrefix)
- throws IllegalArgumentException {
- throw new UnsupportedOperationException();
- }
-
- public Object getProperty(String name) {
- throw new UnsupportedOperationException();
- }
-
- public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void processingInstruction(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void setFeature(String name, boolean state) throws IllegalArgumentException,
- IllegalStateException {
- if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
- mIndent = true;
- return;
- }
- throw new UnsupportedOperationException();
- }
-
- public void setOutput(OutputStream os, String encoding) throws IOException,
- IllegalArgumentException, IllegalStateException {
- if (os == null)
- throw new IllegalArgumentException();
- if (true) {
- try {
- mCharset = Charset.forName(encoding).newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- } catch (IllegalCharsetNameException e) {
- throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
- encoding).initCause(e));
- } catch (UnsupportedCharsetException e) {
- throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
- encoding).initCause(e));
- }
- mOutputStream = os;
- } else {
- setOutput(
- encoding == null
- ? new OutputStreamWriter(os)
- : new OutputStreamWriter(os, encoding));
- }
- }
-
- public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
- IllegalStateException {
- mWriter = writer;
- }
-
- public void setPrefix(String prefix, String namespace) throws IOException,
- IllegalArgumentException, IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void setProperty(String name, Object value) throws IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void startDocument(String encoding, Boolean standalone) throws IOException,
- IllegalArgumentException, IllegalStateException {
- append("<?xml version='1.0' encoding='utf-8'");
- if (standalone != null) {
- append(" standalone='" + (standalone ? "yes" : "no") + "'");
- }
- append(" ?>\n");
- mLineStart = true;
- }
-
- public XmlSerializer startTag(String namespace, String name) throws IOException,
- IllegalArgumentException, IllegalStateException {
- if (mInTag) {
- append(">\n");
- }
- if (mIndent) {
- appendIndent(mNesting);
- }
- mNesting++;
- append('<');
- if (namespace != null) {
- append(namespace);
- append(':');
- }
- append(name);
- mInTag = true;
- mLineStart = false;
- return this;
- }
-
- public XmlSerializer text(char[] buf, int start, int len) throws IOException,
- IllegalArgumentException, IllegalStateException {
- if (mInTag) {
- append(">");
- mInTag = false;
- }
- escapeAndAppendString(buf, start, len);
- if (mIndent) {
- mLineStart = buf[start+len-1] == '\n';
- }
- return this;
- }
-
- public XmlSerializer text(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- if (mInTag) {
- append(">");
- mInTag = false;
- }
- escapeAndAppendString(text);
- if (mIndent) {
- mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
- }
- return this;
- }
-
-}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 87324db..58ee2b2 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -19,6 +19,7 @@
import static android.os.Trace.TRACE_TAG_APP;
import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
@@ -228,6 +229,11 @@
*/
public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24;
+ /**
+ * Time it takes to start back preview surface animation after a back gesture starts.
+ */
+ public static final int ACTION_BACK_SYSTEM_ANIMATION = 25;
+
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
ACTION_TOGGLE_RECENTS,
@@ -254,6 +260,7 @@
ACTION_SMARTSPACE_DOORBELL,
ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
+ ACTION_BACK_SYSTEM_ANIMATION,
};
/** @hide */
@@ -283,6 +290,7 @@
ACTION_SMARTSPACE_DOORBELL,
ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
+ ACTION_BACK_SYSTEM_ANIMATION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
@@ -315,6 +323,7 @@
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION,
};
private final Object mLock = new Object();
@@ -503,6 +512,8 @@
return "ACTION_NOTIFICATION_BIG_PICTURE_LOADED";
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME:
return "ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME";
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION:
+ return "ACTION_BACK_SYSTEM_ANIMATION";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 3795fc8..440a332 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -43,7 +43,10 @@
"-Wall",
"-Werror",
+ "-Wextra",
+ "-Wthread-safety",
"-Wno-error=deprecated-declarations",
+ "-Wno-unused-parameter",
"-Wunused",
"-Wunreachable-code",
diff --git a/core/jni/android_content_res_ResourceTimer.cpp b/core/jni/android_content_res_ResourceTimer.cpp
index 91e3c921..66bda61 100644
--- a/core/jni/android_content_res_ResourceTimer.cpp
+++ b/core/jni/android_content_res_ResourceTimer.cpp
@@ -44,9 +44,9 @@
static int NativeGetTimers(JNIEnv* env, jobject /*clazz*/, jobjectArray timer, jboolean reset) {
size_t size = ResourceTimer::counterSize;
- if (jsize st = env->GetArrayLength(timer); st < size) {
- // Shrink the size to the minimum of the available counters and the available space.
- size = st;
+ if (size_t st = env->GetArrayLength(timer); st < size) {
+ // Shrink the size to the minimum of the available counters and the available space.
+ size = st;
}
for (size_t i = 0; i < size; i++) {
ResourceTimer::Timer src;
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 30e546c..82570be8 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1892,8 +1892,8 @@
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_NOISE_PROFILE);
- const status_t numPlaneColors = isBayer ? 3 : 1;
- const status_t numCfaChannels = isBayer ? 4 : 1;
+ const unsigned long numPlaneColors = isBayer ? 3 : 1;
+ const unsigned long numCfaChannels = isBayer ? 4 : 1;
uint8_t cfaOut[numCfaChannels];
if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
diff --git a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
index 1390759..4bde87f 100644
--- a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
+++ b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
@@ -599,7 +599,7 @@
quality, cropLeft, cropTop, cropRight, cropBottom, rot90);
size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob);
- if (finalJpegSize > outBufCapacity) {
+ if (finalJpegSize > static_cast<size_t>(outBufCapacity)) {
ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\
"capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity);
return actualJpegSize;
diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp
index 4b563d7..7c9dae0 100644
--- a/core/jni/android_media_AudioProductStrategies.cpp
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -88,12 +88,12 @@
int attrGroupIndex = 0;
std::map<int /**attributesGroupIndex*/, std::vector<VolumeGroupAttributes> > groups;
for (const auto &attr : strategy.getVolumeGroupAttributes()) {
- int groupId = attr.getGroupId();
+ auto groupId = attr.getGroupId();
int streamType = attr.getStreamType();
const auto &iter = std::find_if(begin(groups), end(groups),
[groupId, streamType](const auto &iter) {
const auto &frontAttr = iter.second.front();
- return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType;
+ return (frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType);
});
// Same Volume Group Id and same stream type
if (iter != end(groups)) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 6440cc3..3413ede 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2501,7 +2501,7 @@
int *nativeArray = nullptr;
nativeArray = env->GetIntArrayElements(jArray, 0);
if (nativeArray != nullptr) {
- for (size_t i = 0; i < len; i++) {
+ for (size_t i = 0; i < static_cast<size_t>(len); i++) {
nativeVector.push_back(nativeArray[i]);
}
env->ReleaseIntArrayElements(jArray, nativeArray, 0);
@@ -2565,7 +2565,7 @@
if (nativeSystemUsages != nullptr) {
jsize len = env->GetArrayLength(systemUsages);
- for (size_t i = 0; i < len; i++) {
+ for (size_t i = 0; i < static_cast<size_t>(len); i++) {
audio_usage_t nativeAudioUsage =
static_cast<audio_usage_t>(nativeSystemUsages[i]);
nativeSystemUsagesVector.push_back(nativeAudioUsage);
@@ -2776,7 +2776,7 @@
return jStatus;
}
- if (devices.size() > maxResultSize) {
+ if (devices.size() > static_cast<size_t>(maxResultSize)) {
return AUDIO_JAVA_INVALID_OPERATION;
}
size_t index = 0;
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 9bd0700..47b4a46 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -221,7 +221,7 @@
ssize_t rc = SendFileDescriptorVector(fd, buffer, len, fds);
- while (rc != len) {
+ while (rc != static_cast<ssize_t>(len)) {
if (rc == -1) {
jniThrowIOException(env, errno);
return -1;
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index a9db91b..e554b44 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -265,7 +265,7 @@
// justify passing such a large amount of data over this path. So the
// alternative (updating the constructor and other code to accept other
// types, should also probably not be taken in this case).
- CHECK_LE(size, std::numeric_limits<jint>::max());
+ CHECK_LE(size, static_cast<size_t>(std::numeric_limits<jint>::max()));
return env->NewObject(clazz.get(), constructID, static_cast<jint>(size));
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 4d8dac1..3539476 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -691,7 +691,7 @@
// String tries to allocate itself on the stack, within a known size, but will
// make a heap allocation if not.
-template <size_t StackReserve>
+template <jsize StackReserve>
class StackString {
public:
StackString(JNIEnv* env, jstring str) : mEnv(env), mJStr(str) {
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index 27c4cd4..95bf49f 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -224,7 +224,7 @@
return nullptr;
}
jint* threadIds = env->GetIntArrayElements(jintArr, 0);
- for (int i = 0; i < size; ++i) {
+ for (size_t i = 0; i < size; ++i) {
threadIds[i] = tidsVector[i];
}
env->ReleaseIntArrayElements(jintArr, threadIds, 0);
diff --git a/core/jni/android_util_CharsetUtils.cpp b/core/jni/android_util_CharsetUtils.cpp
index 7ab6e8f2..7071cf2 100644
--- a/core/jni/android_util_CharsetUtils.cpp
+++ b/core/jni/android_util_CharsetUtils.cpp
@@ -25,7 +25,7 @@
// Quickly check if destination has plenty of room for worst-case
// 4-bytes-per-char encoded size
- const size_t worstLen = (srcLen * 4);
+ const jint worstLen = (srcLen * 4);
if (destOff >= 0 && destOff + worstLen < destLen) {
env->GetStringUTFRegion(src, 0, srcLen, destPtr + destOff);
return strlen(destPtr + destOff + srcLen) + srcLen;
@@ -33,7 +33,7 @@
// String still might fit in destination, but we need to measure
// its actual encoded size to be sure
- const size_t encodedLen = env->GetStringUTFLength(src);
+ const jint encodedLen = env->GetStringUTFLength(src);
if (destOff >= 0 && destOff + encodedLen < destLen) {
env->GetStringUTFRegion(src, 0, srcLen, destPtr + destOff);
return encodedLen;
diff --git a/core/jni/android_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp
index c64c212..8d3da7b 100644
--- a/core/jni/android_util_FileObserver.cpp
+++ b/core/jni/android_util_FileObserver.cpp
@@ -114,7 +114,7 @@
if (fd >= 0)
{
size_t count = wfds.size();
- for (jsize i = 0; i < count; ++i) {
+ for (size_t i = 0; i < count; ++i) {
jstring pathString = (jstring) env->GetObjectArrayElement(pathStrings, i);
ScopedUtfChars path(env, pathString);
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 41c65ae..fef8ad7 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -140,7 +140,7 @@
env->ExceptionClear();
return NULL;
}
- for (int i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
ScopedLocalRef<jobject>
frameTimelineObj(env,
@@ -193,7 +193,7 @@
gDisplayEventReceiverClassInfo
.vsyncEventDataClassInfo
.frameTimelines)));
- for (int i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
ScopedLocalRef<jobject>
frameTimelineObj(env, env->GetObjectArrayElement(frameTimelinesObj.get(), i));
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9833598..a800e6e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1950,7 +1950,7 @@
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
- for (int i = 0; i < jankData.size(); i++) {
+ for (size_t i = 0; i < jankData.size(); i++) {
jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index 55995df..bc69d1e6 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -67,7 +67,7 @@
static jobjectArray fromWindowInfos(JNIEnv* env, const std::vector<WindowInfo>& windowInfos) {
jobjectArray jWindowHandlesArray =
env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
- for (int i = 0; i < windowInfos.size(); i++) {
+ for (size_t i = 0; i < windowInfos.size(); i++) {
ScopedLocalRef<jobject>
jWindowHandle(env,
android_view_InputWindowHandle_fromWindowInfo(env, windowInfos[i]));
@@ -80,7 +80,7 @@
static jobjectArray fromDisplayInfos(JNIEnv* env, const std::vector<DisplayInfo>& displayInfos) {
jobjectArray jDisplayInfoArray =
env->NewObjectArray(displayInfos.size(), gDisplayInfoClassInfo.clazz, nullptr);
- for (int i = 0; i < displayInfos.size(); i++) {
+ for (size_t i = 0; i < displayInfos.size(); i++) {
ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i]));
env->SetObjectArrayElement(jDisplayInfoArray, i, jDisplayInfo.get());
}
diff --git a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp
index d4f6e18..0c39a69 100644
--- a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp
+++ b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp
@@ -266,11 +266,11 @@
auto jsResourceName = reinterpret_cast<jstring>(
env->GetObjectField(entry, gFabricatedOverlayInternalEntryOffsets.resourceName));
const ScopedUtfChars resourceName(env, jsResourceName);
- const auto dataType =
+ const jint dataType =
env->GetIntField(entry, gFabricatedOverlayInternalEntryOffsets.dataType);
// In Java, the data type is int but the maximum value of data Type is less than 0xff.
- if (dataType >= UCHAR_MAX) {
+ if (dataType >= static_cast<jint>(UCHAR_MAX)) {
jniThrowException(env, IllegalArgumentException, "Unsupported data type");
return;
}
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
index 098a4d8..cf70c90 100644
--- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -55,7 +55,7 @@
static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
jint endUid) {
- for (uint32_t uid = startUid; uid <= endUid; ++uid) {
+ for (jint uid = startUid; uid <= endUid; ++uid) {
if (!android::bpf::clearUidTimes(uid)) return false;
}
return true;
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
index dfae684..be9013b 100644
--- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -147,7 +147,7 @@
std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
- for (int i = 0; i < selectedThreadIds.size(); i++) {
+ for (size_t i = 0; i < selectedThreadIds.size(); i++) {
if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i],
SELECTED_THREAD_AGGREGATION_KEY)) {
return false;
@@ -312,11 +312,11 @@
auto fields = android::base::Split(line.c_str(), ":");
android::base::ParseUint(fields[0], &aggregationKey);
- for (int j = 1; j < fields.size(); j++) {
+ for (size_t j = 1; j < fields.size(); j++) {
auto numbers = android::base::Split(fields[j], " ");
std::vector<uint64_t> chunk;
- for (int k = 0; k < numbers.size(); k++) {
+ for (size_t k = 0; k < numbers.size(); k++) {
uint64_t time;
android::base::ParseUint(numbers[k], &time);
chunk.emplace_back(time);
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 1f29735..dab47e9 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -288,7 +288,7 @@
}
bool nonZero = false;
- for (int i = 0; i < vector->size(); i++) {
+ for (size_t i = 0; i < vector->size(); i++) {
jint index = scopedIndexMap[i];
if (index < 0 || index >= size) {
jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9c1bea7..7c5885a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1094,8 +1094,8 @@
}
struct dirent* ent;
while ((ent = readdir(dir.get()))) {
- if (ent->d_ino == ce_data_inode) {
- return ent->d_name;
+ if (static_cast<long long>(ent->d_ino) == ce_data_inode) {
+ return ent->d_name;
}
}
}
@@ -1765,14 +1765,14 @@
static void ReloadBuildJavaConstants(JNIEnv* env) {
jclass build_cls = env->FindClass("android/os/Build");
size_t arr_size = sizeof(build_constants) / sizeof(build_constants[0]);
- for (int i = 0; i < arr_size; i++) {
+ for (size_t i = 0; i < arr_size; i++) {
const char* field_name = build_constants[i].first;
const char* sysprop_name = build_constants[i].second;
ReloadBuildJavaConstant(env, build_cls, field_name, "Ljava/lang/String;", sysprop_name);
}
jclass build_version_cls = env->FindClass("android/os/Build$VERSION");
arr_size = sizeof(build_version_constants) / sizeof(build_version_constants[0]);
- for (int i = 0; i < arr_size; i++) {
+ for (size_t i = 0; i < arr_size; i++) {
const char* field_name = build_version_constants[i].first;
const char* sysprop_name = build_version_constants[i].second;
ReloadBuildJavaConstant(env, build_version_cls, field_name, "Ljava/lang/String;", sysprop_name);
@@ -2901,7 +2901,7 @@
return -1;
}
ScopedByteArrayRO source(env, in);
- if (source.size() < length) {
+ if (source.size() < static_cast<size_t>(length)) {
// Invalid parameter
jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
return -1;
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 2b5b8f7..87ab496 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -76,7 +76,7 @@
return {};
}
fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno)));
- } else if (nread == MAX_COMMAND_BYTES - mEnd) {
+ } else if (nread == static_cast<ssize_t>(MAX_COMMAND_BYTES - mEnd)) {
// This is pessimistic by one character, but close enough.
fail_fn("ZygoteCommandBuffer overflowed: command too long");
}
@@ -136,7 +136,7 @@
}
char* countString = line.value().first; // Newline terminated.
long nArgs = atol(countString);
- if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) {
+ if (nArgs <= 0 || nArgs >= static_cast<long>(MAX_COMMAND_BYTES / 2)) {
fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs));
}
mLinesLeft = nArgs;
@@ -153,7 +153,7 @@
// As a side effect, this sets mNiceName to a non-empty string, if possible.
template<class FailFn>
bool isSimpleForkCommand(int minUid, FailFn fail_fn) {
- if (mLinesLeft <= 0 || mLinesLeft >= MAX_COMMAND_BYTES / 2) {
+ if (mLinesLeft <= 0 || mLinesLeft >= static_cast<int32_t>(MAX_COMMAND_BYTES / 2)) {
return false;
}
static const char* RUNTIME_ARGS = "--runtime-args";
@@ -179,14 +179,14 @@
if (!read_result.has_value()) {
return false;
}
- auto [arg_start, arg_end] = read_result.value();
- if (arg_end - arg_start == RA_LENGTH
- && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
+ const auto [arg_start, arg_end] = read_result.value();
+ if (static_cast<size_t>(arg_end - arg_start) == RA_LENGTH &&
+ strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
saw_runtime_args = true;
continue;
}
- if (arg_end - arg_start >= NN_LENGTH
- && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= NN_LENGTH &&
+ strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
size_t name_len = arg_end - (arg_start + NN_LENGTH);
size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1);
memcpy(mNiceName, arg_start + NN_LENGTH, copy_len);
@@ -196,21 +196,21 @@
}
continue;
}
- if (arg_end - arg_start == IW_LENGTH
- && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) == IW_LENGTH &&
+ strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
// This also removes the need for invoke-with security checks here.
return false;
}
- if (arg_end - arg_start == CZ_LENGTH
- && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) == CZ_LENGTH &&
+ strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
return false;
}
- if (arg_end - arg_start >= CA_LENGTH
- && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= CA_LENGTH &&
+ strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
return false;
}
- if (arg_end - arg_start >= SU_LENGTH
- && strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= SU_LENGTH &&
+ strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
int uid = digitsVal(arg_start + SU_LENGTH, arg_end);
if (uid < minUid) {
return false;
@@ -218,8 +218,8 @@
saw_setuid = true;
continue;
}
- if (arg_end - arg_start >= SG_LENGTH
- && strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= SG_LENGTH &&
+ strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
int gid = digitsVal(arg_start + SG_LENGTH, arg_end);
if (gid == -1) {
return false;
@@ -422,7 +422,7 @@
bool first_time = true;
do {
- if (credentials.uid != expected_uid) {
+ if (credentials.uid != static_cast<uid_t>(expected_uid)) {
return JNI_FALSE;
}
n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 8e619a8..404fa39 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -593,6 +593,7 @@
optional bool seamless_rotating = 13;
optional int64 finish_seamless_rotate_frame_number = 14;
optional bool controllable = 15;
+ optional WindowStateProto source_window_state = 16;
}
message ImeInsetsSourceProviderProto {
diff --git a/core/proto/android/view/insetssource.proto b/core/proto/android/view/insetssource.proto
index 41b9f43..e6c6d59 100644
--- a/core/proto/android/view/insetssource.proto
+++ b/core/proto/android/view/insetssource.proto
@@ -30,4 +30,5 @@
optional .android.graphics.RectProto frame = 2;
optional .android.graphics.RectProto visible_frame = 3;
optional bool visible = 4;
+ optional int32 type_number = 5;
}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ab0ef7d..6859f1f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2247,6 +2247,13 @@
<permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows changing Thread network state and access to Thread network
+ credentials such as Network Key and PSKc.
+ <p>Not for use by third-party applications.
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled") -->
+ <permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
<!-- #SystemApi @hide Allows an app to bypass Private DNS.
<p>Not for use by third-party applications.
TODO: publish as system API in next API release. -->
@@ -7782,6 +7789,15 @@
<permission android:name="android.permission.WRITE_FLAGS"
android:protectionLevel="signature" />
+ <!-- @hide @SystemApi
+ @FlaggedApi("android.app.get_binding_uid_importance")
+ Allows to get the importance of an UID that has a service
+ binding to the app.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @hide Allows internal applications to manage displays.
<p>This means intercept internal signals about displays being (dis-)connected
and being able to enable or disable the external displays.
diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_current_locale_item.xml
index 990e26c..01b9cc5 100644
--- a/core/res/res/layout/app_language_picker_current_locale_item.xml
+++ b/core/res/res/layout/app_language_picker_current_locale_item.xml
@@ -39,6 +39,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_check_24dp"
- app:tint="?attr/colorAccentPrimaryVariant"/>
+ app:tint="?attr/colorAccentPrimaryVariant"
+ android:contentDescription="@*android:string/checked"/>
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index c0c6e05..30beee0 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -568,6 +568,10 @@
<color name="side_fps_button_color">#00677E</color>
<!-- Color for system bars -->
+ <color name="status_bar_compatible">@android:color/black</color>
+ <!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
+ color is set by the framework or not. -->
+ <color name="status_bar_default">#00808080</color>
<color name="navigation_bar_compatible">@android:color/black</color>
<!-- This uses non-regular transparent intentionally. It is used to tell if the transparent
color is set by the framework or not. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 862e537..f827d28 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -832,6 +832,13 @@
is set to true. -->
<bool name="config_remoteInsetsControllerSystemBarsCanBeShownByUserAction">false</bool>
+ <!-- To change the default behavior of how the insets get involved when calculating
+ configuration. This will no longer consider status bar and display cutout, and only
+ exclude navigation bars from the screen sizes. This is useful when the display is close to
+ square and the system bars may cause the orientation with non-preferred value.
+ -->
+ <bool name="config_decoupleStatusBarAndDisplayCutoutFromScreenSize">false</bool>
+
<!-- HDMI behavior -->
<!-- The number of degrees to rotate the display when the device has HDMI connected
@@ -1256,6 +1263,11 @@
-->
<bool name="config_shortPressEarlyOnPower">false</bool>
+ <!-- Whether a single short press on STEM_PRIMARY should be launched without multi-press delay.
+ This works similarly as config_shortPressEarlyOnPower but for STEM_PRIMARY.
+ -->
+ <bool name="config_shortPressEarlyOnStemPrimary">false</bool>
+
<!-- Control the behavior of the search key.
0 - Launch default search activity
1 - Launch target activity defined by config_searchKeyTargetActivity
@@ -2707,6 +2719,12 @@
turned off and the screen off animation has been performed. -->
<bool name="config_dozeAfterScreenOffByDefault">false</bool>
+ <!-- If true, bright policy will be applied when we have entered dozing wakefulness but haven't
+ started doze component. This can be used to suppress the temporary dim state before
+ starting a dream service. This is typically used together with
+ config_maximumScreenDimDuration set to 0 to completely suppress dim effect. -->
+ <bool name="config_brightWhenDozing">false</bool>
+
<!-- Doze: should the TYPE_PICK_UP_GESTURE sensor be used as a pulse signal. -->
<bool name="config_dozePulsePickup">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e646548..2f3b510 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -473,6 +473,7 @@
<java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
<java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
<java-symbol type="bool" name="config_shortPressEarlyOnPower" />
+ <java-symbol type="bool" name="config_shortPressEarlyOnStemPrimary" />
<java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
<java-symbol type="integer" name="config_searchKeyBehavior" />
<java-symbol type="string" name="config_searchKeyTargetActivity" />
@@ -1732,6 +1733,7 @@
<java-symbol type="bool" name="config_enableLockScreenRotation" />
<java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
<java-symbol type="bool" name="config_remoteInsetsControllerSystemBarsCanBeShownByUserAction" />
+ <java-symbol type="bool" name="config_decoupleStatusBarAndDisplayCutoutFromScreenSize" />
<java-symbol type="bool" name="config_lidControlsScreenLock" />
<java-symbol type="bool" name="config_lidControlsSleep" />
<java-symbol type="bool" name="config_lockDayNightMode" />
@@ -1955,6 +1957,7 @@
<java-symbol type="bool" name="config_enableNightMode" />
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
+ <java-symbol type="bool" name="config_brightWhenDozing" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
<java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
@@ -3074,6 +3077,8 @@
<java-symbol type="bool" name="config_navBarDefaultTransparent" />
<java-symbol type="color" name="navigation_bar_default"/>
<java-symbol type="color" name="navigation_bar_compatible"/>
+ <java-symbol type="color" name="status_bar_default"/>
+ <java-symbol type="color" name="status_bar_compatible"/>
<!-- EditText suggestion popup. -->
<java-symbol type="id" name="suggestionWindowContainer" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index bdbf96b..d5d67ab 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -190,7 +190,7 @@
<item name="windowTranslucentStatus">false</item>
<item name="windowTranslucentNavigation">false</item>
<item name="windowDrawsSystemBarBackgrounds">false</item>
- <item name="statusBarColor">@color/black</item>
+ <item name="statusBarColor">@color/status_bar_default</item>
<item name="navigationBarColor">@color/navigation_bar_default</item>
<item name="windowActionBarFullscreenDecorLayout">@layout/screen_action_bar</item>
<item name="windowContentTransitions">false</item>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 3a2e50a..709646b 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
<!-- Arab Emirates -->
- <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" />
+ <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" />
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -155,7 +155,7 @@
<shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
<!-- Israel: 4 digits, known premium codes listed -->
- <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
+ <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477" />
<!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
@@ -198,6 +198,9 @@
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
+ <!-- Namibia: 5 digits -->
+ <shortcode country="na" pattern="\\d{1,5}" free="40005" />
+
<!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
<shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index 282fdad..ba2ea88 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -125,6 +125,9 @@
Field configActivity = Class.forName(CLASS).getDeclaredField("configurationActivity");
configActivity.setAccessible(true);
configActivity.set(rule, new ComponentName(longString, longString));
+ Field trigger = Class.forName(CLASS).getDeclaredField("mTriggerDescription");
+ trigger.setAccessible(true);
+ trigger.set(rule, longString);
} catch (NoSuchFieldException e) {
fail(e.toString());
} catch (ClassNotFoundException e) {
@@ -149,5 +152,6 @@
fromParcel.getOwner().getPackageName().length());
assertEquals(AutomaticZenRule.MAX_STRING_LENGTH,
fromParcel.getOwner().getClassName().length());
+ assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length());
}
}
diff --git a/core/tests/coretests/src/android/app/time/TEST_MAPPING b/core/tests/coretests/src/android/app/time/TEST_MAPPING
deleted file mode 100644
index 9d711a2..0000000
--- a/core/tests/coretests/src/android/app/time/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.app.time."
- }
- ]
- }
- ]
-}
diff --git a/core/tests/coretests/src/android/app/timedetector/OWNERS b/core/tests/coretests/src/android/app/timedetector/OWNERS
deleted file mode 100644
index c612473..0000000
--- a/core/tests/coretests/src/android/app/timedetector/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING b/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING
deleted file mode 100644
index 6c4d48d..0000000
--- a/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.app.timedetector."
- }
- ]
- }
- ]
-}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/OWNERS b/core/tests/coretests/src/android/app/timezonedetector/OWNERS
deleted file mode 100644
index 2e9c324..0000000
--- a/core/tests/coretests/src/android/app/timezonedetector/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /core/java/android/app/timezonedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING b/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING
deleted file mode 100644
index 8872f64..0000000
--- a/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.app.timezonedetector."
- }
- ]
- }
- ]
-}
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index 7ebda00..bec72d9 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -10,7 +10,6 @@
{"include-filter": "android.service.notification"},
{"include-filter": "android.service.quicksettings"},
{"include-filter": "android.service.settings.suggestions"},
- {"include-filter": "android.service.timezone"},
{"exclude-annotation": "org.junit.Ignore"}
]
}
diff --git a/core/tests/coretests/src/android/service/timezone/OWNERS b/core/tests/coretests/src/android/service/timezone/OWNERS
deleted file mode 100644
index 8116388..0000000
--- a/core/tests/coretests/src/android/service/timezone/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /core/java/android/service/timezone/OWNERS
diff --git a/core/tests/coretests/src/android/service/timezone/TEST_MAPPING b/core/tests/coretests/src/android/service/timezone/TEST_MAPPING
deleted file mode 100644
index 46f476f..0000000
--- a/core/tests/coretests/src/android/service/timezone/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksCoreTests",
- "options": [
- {
- "include-filter": "android.service.timezone."
- }
- ]
- }
- ]
-}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index e7117a7..dfe6cf8 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -49,6 +49,7 @@
import android.app.Instrumentation;
import android.app.UiModeManager;
import android.content.Context;
+import android.graphics.ForceDarkType;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.SystemProperties;
@@ -593,7 +594,7 @@
mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
);
- assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
+ assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
}
@Test
@@ -613,7 +614,8 @@
mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
);
- assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
+ assertThat(mViewRootImpl.determineForceDarkType())
+ .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK);
}
@Test
@@ -634,7 +636,7 @@
mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
);
- assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
+ assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
}
@Test
@@ -654,7 +656,7 @@
mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
);
- assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
+ assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
}
private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
diff --git a/core/tests/timetests/Android.bp b/core/tests/timetests/Android.bp
new file mode 100644
index 0000000..51181a8
--- /dev/null
+++ b/core/tests/timetests/Android.bp
@@ -0,0 +1,25 @@
+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"],
+}
+
+android_test {
+ name: "FrameworksTimeCoreTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "device-time-shell-utils",
+ "junit",
+ "junit-params",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "truth",
+ ],
+ libs: ["android.test.runner"],
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/core/tests/timetests/AndroidManifest.xml b/core/tests/timetests/AndroidManifest.xml
new file mode 100644
index 0000000..330e05f
--- /dev/null
+++ b/core/tests/timetests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.time">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.coretests.time"
+ android:label="Frameworks Time Core Tests" />
+
+</manifest>
diff --git a/core/tests/timetests/AndroidTest.xml b/core/tests/timetests/AndroidTest.xml
new file mode 100644
index 0000000..d2d1255
--- /dev/null
+++ b/core/tests/timetests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Time Core Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksTimeCoreTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.coretests.time" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/core/tests/coretests/src/android/app/time/OWNERS b/core/tests/timetests/OWNERS
similarity index 100%
rename from core/tests/coretests/src/android/app/time/OWNERS
rename to core/tests/timetests/OWNERS
diff --git a/core/tests/timetests/TEST_MAPPING b/core/tests/timetests/TEST_MAPPING
new file mode 100644
index 0000000..5748044
--- /dev/null
+++ b/core/tests/timetests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+ "postsubmit": [
+ {
+ "name": "FrameworksTimeCoreTests"
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java b/core/tests/timetests/src/android/app/time/DetectorStatusTypesTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java
rename to core/tests/timetests/src/android/app/time/DetectorStatusTypesTest.java
diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/timetests/src/android/app/time/ExternalTimeSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
rename to core/tests/timetests/src/android/app/time/ExternalTimeSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java b/core/tests/timetests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
rename to core/tests/timetests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
diff --git a/core/tests/coretests/src/android/app/time/ParcelableTestSupport.java b/core/tests/timetests/src/android/app/time/ParcelableTestSupport.java
similarity index 93%
rename from core/tests/coretests/src/android/app/time/ParcelableTestSupport.java
rename to core/tests/timetests/src/android/app/time/ParcelableTestSupport.java
index 13e5e14..d2c9c3a 100644
--- a/core/tests/coretests/src/android/app/time/ParcelableTestSupport.java
+++ b/core/tests/timetests/src/android/app/time/ParcelableTestSupport.java
@@ -47,6 +47,10 @@
return toReturn;
}
+ /**
+ * Asserts that the parameter can be parceled and unparceled and return an object considered
+ * equal to the original.
+ */
public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
assertEqualsAndHashCode(instance, roundTripParcelable(instance));
}
diff --git a/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java b/core/tests/timetests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
rename to core/tests/timetests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeCapabilitiesTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
rename to core/tests/timetests/src/android/app/time/TimeCapabilitiesTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeManagerTest.java b/core/tests/timetests/src/android/app/time/TimeManagerTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeManagerTest.java
rename to core/tests/timetests/src/android/app/time/TimeManagerTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeStateTest.java b/core/tests/timetests/src/android/app/time/TimeStateTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeStateTest.java
rename to core/tests/timetests/src/android/app/time/TimeStateTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java b/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java b/core/tests/timetests/src/android/app/time/TimeZoneDetectorStatusTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneDetectorStatusTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java b/core/tests/timetests/src/android/app/time/TimeZoneStateTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneStateTest.java
diff --git a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java b/core/tests/timetests/src/android/app/time/UnixEpochTimeTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
rename to core/tests/timetests/src/android/app/time/UnixEpochTimeTest.java
diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/timetests/src/android/app/timedetector/ManualTimeSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
rename to core/tests/timetests/src/android/app/timedetector/ManualTimeSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java b/core/tests/timetests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
rename to core/tests/timetests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java b/core/tests/timetests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
rename to core/tests/timetests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java b/core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java
similarity index 89%
rename from core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java
rename to core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java
index 4efaed1..59a7304 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java
+++ b/core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java
@@ -29,10 +29,17 @@
public final class ShellCommandTestSupport {
private ShellCommandTestSupport() {}
+ /**
+ * Returns a {@link ShellCommand} from the supplied String, where elements of the command are
+ * separated with spaces. No escaping is performed.
+ */
public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) {
return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" ")));
}
+ /**
+ * Returns a {@link ShellCommand} from the supplied list of command line elements.
+ */
public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) {
ShellCommand command = mock(ShellCommand.class);
class ArgProvider {
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java b/core/tests/timetests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
rename to core/tests/timetests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderEventTest.java
similarity index 100%
rename from core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java
rename to core/tests/timetests/src/android/service/timezone/TimeZoneProviderEventTest.java
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java
similarity index 98%
rename from core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
rename to core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java
index 9b24559..cf37db6 100644
--- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
+++ b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java
@@ -32,6 +32,9 @@
import androidx.test.filters.SmallTest;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,9 +42,6 @@
import java.util.List;
import java.util.stream.IntStream;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
/** Non-SDK tests. See CTS for SDK API tests. */
@RunWith(JUnitParamsRunner.class)
@SmallTest
@@ -83,6 +83,7 @@
providerStatus.couldEnableTelephonyFallback());
}
+ /** Parameters for {@link #couldEnableTelephonyFallback}. */
public static Integer[][] couldEnableTelephonyFallbackParams() {
List<Integer[]> params = new ArrayList<>();
@DependencyStatus int[] dependencyStatuses =
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java
rename to core/tests/timetests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 69aa401..32186667 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -439,6 +439,8 @@
<permission name="android.permission.MANAGE_WIFI_NETWORK_SELECTION" />
<!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo -->
<permission name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+ <!-- Permission required for CTS test - CtsThreadNetworkTestCases -->
+ <permission name="android.permission.THREAD_NETWORK_PRIVILEGED"/>
<!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
<!-- Permission required for CTS test - MusicRecognitionManagerTest -->
@@ -529,6 +531,7 @@
<permission name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
<!-- Permission required for CTS test IntentRedirectionTest -->
<permission name="android.permission.QUERY_CLONED_APPS"/>
+ <permission name="android.permission.GET_BINDING_UID_IMPORTANCE"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt
index 8e76fd2..48c0a2d 100644
--- a/framework-minus-apex-ravenwood-policies.txt
+++ b/framework-minus-apex-ravenwood-policies.txt
@@ -71,8 +71,9 @@
# Misc
class android.util.Dumpable stubclass
class android.util.DebugUtils stubclass
-class android.util.UtilConfig stubclass
+class android.util.MathUtils stubclass
class android.util.Patterns stubclass
+class android.util.UtilConfig stubclass
# Internals
class com.android.internal.util.ArrayUtils stubclass
@@ -89,3 +90,26 @@
class com.android.internal.util.LineBreakBufferedWriter stubclass
class com.android.internal.util.Preconditions stubclass
class com.android.internal.util.StringPool stubclass
+
+# Parcel
+class android.os.Parcel stubclass
+ method writeException (Ljava/lang/Exception;)V @writeException$ravenwood
+ method writeNoException ()V @writeNoException$ravenwood
+class android.os.Parcel !com.android.hoststubgen.nativesubstitution.Parcel_host
+
+class android.os.Parcelable stubclass
+class android.os.ParcelFormatException stubclass
+class android.os.BadParcelableException stubclass
+class android.os.BadTypeParcelableException stubclass
+
+# Binder: just enough to construct, no further functionality
+class android.os.Binder stub
+ method <init> ()V stub
+ method <init> (Ljava/lang/String;)V stub
+ method isDirectlyHandlingTransaction ()Z stub
+ method isDirectlyHandlingTransactionNative ()Z @isDirectlyHandlingTransactionNative$ravenwood
+ method getNativeBBinderHolder ()J @getNativeBBinderHolder$ravenwood
+
+# Containers
+class android.os.BaseBundle stubclass
+class android.os.Bundle stubclass
diff --git a/graphics/java/android/graphics/ForceDarkType.java b/graphics/java/android/graphics/ForceDarkType.java
new file mode 100644
index 0000000..396b037
--- /dev/null
+++ b/graphics/java/android/graphics/ForceDarkType.java
@@ -0,0 +1,60 @@
+/*
+ * 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.graphics;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The style of force dark to use in {@link HardwareRenderer}.
+ *
+ * You must keep this in sync with the C++ enum ForceDarkType in
+ * frameworks/base/libs/hwui/utils/ForceDark.h
+ *
+ * @hide
+ */
+public class ForceDarkType {
+ /**
+ * Force dark disabled: normal, default operation.
+ *
+ * @hide
+ */
+ public static final int NONE = 0;
+
+ /**
+ * Use force dark
+ * @hide
+ */
+ public static final int FORCE_DARK = 1;
+
+ /**
+ * Force force-dark. {@see Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED}
+ * @hide */
+ public static final int FORCE_INVERT_COLOR_DARK = 2;
+
+ /** @hide */
+ @IntDef({
+ NONE,
+ FORCE_DARK,
+ FORCE_INVERT_COLOR_DARK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ForceDarkTypeDef {}
+
+}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 8cd262e..20e393e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -182,7 +182,7 @@
/** @hide */
protected RenderNode mRootNode;
private boolean mOpaque = true;
- private boolean mForceDark = false;
+ private int mForceDark = ForceDarkType.NONE;
private @ActivityInfo.ColorMode int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
private float mDesiredSdrHdrRatio = 1f;
@@ -571,10 +571,10 @@
* Whether or not the force-dark feature should be used for this renderer.
* @hide
*/
- public boolean setForceDark(boolean enable) {
- if (mForceDark != enable) {
- mForceDark = enable;
- nSetForceDark(mNativeProxy, enable);
+ public boolean setForceDark(@ForceDarkType.ForceDarkTypeDef int type) {
+ if (mForceDark != type) {
+ mForceDark = type;
+ nSetForceDark(mNativeProxy, type);
return true;
}
return false;
@@ -1597,7 +1597,7 @@
private static native void nAllocateBuffers(long nativeProxy);
- private static native void nSetForceDark(long nativeProxy, boolean enabled);
+ private static native void nSetForceDark(long nativeProxy, int type);
private static native void nSetDisplayDensityDpi(int densityDpi);
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 0e59e9a..29bdd5c 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -28,3 +28,10 @@
description: "Enables invoking split contextually"
bug: "276361926"
}
+
+flag {
+ name: "enable_taskbar_navbar_unification"
+ namespace: "multitasking"
+ description: "Enables taskbar / navbar unification"
+ bug: "309671494"
+}
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index b556150e..3e66bbb 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -72,7 +72,7 @@
<!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead [CHAR LIMIT=NONE] -->
<string name="dock_non_resizeble_failed_to_dock_text">App does not support split screen</string>
<!-- Warning message when we try to dock an app not supporting multiple instances split into multiple sides [CHAR LIMIT=NONE] -->
- <string name="dock_multi_instances_not_supported_text">This app can only be opened in 1 window.</string>
+ <string name="dock_multi_instances_not_supported_text">This app can only be opened in 1 window</string>
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] -->
<string name="forced_resizable_secondary_display">App may not work on a secondary display.</string>
<!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index 5cf9175..8241e1a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -136,6 +136,7 @@
/** Called on frame update. */
final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+ mTransformation.clear();
// Extract the transformation to the current time.
mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
mTransformation);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
index 42dc19c..7749394 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
@@ -130,7 +130,14 @@
mStartBounds);
mCustomizer.customizeStatusBarAppearance(region);
} else {
- mCustomizer.customizeStatusBarAppearance(null);
+ resetStatusBarCustomization();
}
}
+
+ /**
+ * Resets the statusbar customization
+ */
+ public void resetStatusBarCustomization() {
+ mCustomizer.customizeStatusBarAppearance(null);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 03c546d..5843635 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -61,6 +61,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
import com.android.wm.shell.animation.FlingAnimationUtils;
import com.android.wm.shell.common.ExternalInterfaceBinder;
@@ -99,6 +100,7 @@
* Max duration to wait for an animation to finish before triggering the real back.
*/
private static final long MAX_ANIMATION_DURATION = 2000;
+ private final LatencyTracker mLatencyTracker;
/** True when a back gesture is ongoing */
private boolean mBackGestureStarted = false;
@@ -167,6 +169,7 @@
private final BackAnimationBackground mAnimationBackground;
private StatusBarCustomizer mCustomizer;
+ private boolean mTrackingLatency;
public BackAnimationController(
@NonNull ShellInit shellInit,
@@ -213,6 +216,7 @@
.setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
.build();
mShellBackAnimationRegistry = shellBackAnimationRegistry;
+ mLatencyTracker = LatencyTracker.getInstance(mContext);
}
private void onInit() {
@@ -438,6 +442,7 @@
private void startBackNavigation(@NonNull TouchTracker touchTracker) {
try {
+ startLatencyTracking();
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
@@ -452,6 +457,7 @@
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo);
if (backNavigationInfo == null) {
ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Received BackNavigationInfo is null.");
+ cancelLatencyTracking();
return;
}
final int backType = backNavigationInfo.getType();
@@ -462,6 +468,8 @@
}
} else {
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
+ // App is handling back animation. Cancel system animation latency tracking.
+ cancelLatencyTracking();
dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
}
}
@@ -808,12 +816,36 @@
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
mActiveCallback = null;
mShellBackAnimationRegistry.resetDefaultCrossActivity();
+ cancelLatencyTracking();
if (mBackNavigationInfo != null) {
mBackNavigationInfo.onBackNavigationFinished(triggerBack);
mBackNavigationInfo = null;
}
}
+ private void startLatencyTracking() {
+ if (mTrackingLatency) {
+ cancelLatencyTracking();
+ }
+ mLatencyTracker.onActionStart(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION);
+ mTrackingLatency = true;
+ }
+
+ private void cancelLatencyTracking() {
+ if (!mTrackingLatency) {
+ return;
+ }
+ mLatencyTracker.onActionCancel(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION);
+ mTrackingLatency = false;
+ }
+
+ private void endLatencyTracking() {
+ if (!mTrackingLatency) {
+ return;
+ }
+ mLatencyTracker.onActionEnd(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION);
+ mTrackingLatency = false;
+ }
private void createAdapter() {
IBackAnimationRunner runner =
@@ -826,6 +858,7 @@
IBackAnimationFinishedCallback finishedCallback) {
mShellExecutor.execute(
() -> {
+ endLatencyTracking();
if (mBackNavigationInfo == null) {
ProtoLog.e(WM_SHELL_BACK_PREVIEW,
"Lack of navigation info to start animation.");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index a596cef..f90e3f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -21,6 +21,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
import static com.android.wm.shell.back.BackAnimationConstants.PROGRESS_COMMIT_THRESHOLD;
+import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import android.animation.Animator;
@@ -191,6 +192,8 @@
// Draw background with task background color.
mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction);
+ setEnteringProgress(0);
+ setLeavingProgress(0);
}
private void applyTransform(SurfaceControl leash, RectF targetRect, float targetAlpha) {
@@ -272,12 +275,16 @@
valueAnimator.addUpdateListener(animation -> {
float progress = animation.getAnimatedFraction();
updatePostCommitEnteringAnimation(progress);
+ if (progress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD) {
+ mBackground.resetStatusBarCustomization();
+ }
mTransaction.apply();
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ mBackground.resetStatusBarCustomization();
finishAnimation();
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 5a15674..80fc3a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -18,8 +18,10 @@
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.window.BackEvent.EDGE_RIGHT;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK;
+import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import android.animation.Animator;
@@ -100,6 +102,7 @@
private RemoteAnimationTarget mClosingTarget;
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
private boolean mBackInProgress = false;
+ private boolean mIsRightEdge;
private final PointF mTouchPos = new PointF();
private IRemoteAnimationFinishedCallback mFinishCallback;
private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
@@ -171,7 +174,12 @@
// Move the window along the Y axis.
float scaledTop = (height - scaledHeight) * 0.5f + deltaY;
// Move the window along the X axis.
- float right = width - (progress * mVerticalMargin);
+ float right;
+ if (mIsRightEdge) {
+ right = (width - scaledWidth) * 0.5f + scaledWidth;
+ } else {
+ right = width - (progress * mVerticalMargin);
+ }
float left = right - scaledWidth;
mClosingCurrentRect.set(left, scaledTop, right, scaledTop + scaledHeight);
@@ -261,6 +269,7 @@
private void onGestureProgress(@NonNull BackEvent backEvent) {
if (!mBackInProgress) {
+ mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
mBackInProgress = true;
}
@@ -287,12 +296,16 @@
float progress = animation.getAnimatedFraction();
updatePostCommitEnteringAnimation(progress);
updatePostCommitClosingAnimation(progress);
+ if (progress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD) {
+ mBackground.resetStatusBarCustomization();
+ }
mTransaction.apply();
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ mBackground.resetStatusBarCustomization();
finishAnimation();
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 9402d02..87461dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1829,9 +1829,12 @@
}
bubble.cleanupViews(); // cleans up the icon view
updateExpandedView(); // resets state for no expanded bubble
+ mExpandedBubble = null;
});
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
+ } else if (getBubbleCount() == 1) {
+ mExpandedBubble = null;
}
// Remove it from the views
for (int i = 0; i < getBubbleCount(); i++) {
@@ -2420,14 +2423,13 @@
mExpandedAnimationController.notifyPreparingToCollapse();
updateOverflowDotVisibility(false /* expanding */);
- final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack(
- mStackAnimationController
- .getStackPositionAlongNearestHorizontalEdge()
- /* collapseTo */,
- () -> {
- mBubbleContainer.setActiveController(mStackAnimationController);
- updateOverflowVisibility();
- });
+ final Runnable collapseBackToStack = () ->
+ mExpandedAnimationController.collapseBackToStack(
+ mStackAnimationController.getStackPositionAlongNearestHorizontalEdge(),
+ () -> {
+ mBubbleContainer.setActiveController(mStackAnimationController);
+ updateOverflowVisibility();
+ });
final Runnable after = () -> {
final BubbleViewProvider previouslySelected = mExpandedBubble;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index beae96e..54cf84c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -838,10 +838,12 @@
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- if (DesktopModeStatus.isEnabled()) {
- return desktopTasksController.map(Lazy::get);
- }
- return Optional.empty();
+ return desktopTasksController.flatMap((lazy)-> {
+ if (DesktopModeStatus.isEnabled()) {
+ return Optional.of(lazy.get());
+ }
+ return Optional.empty();
+ });
}
@BindsOptionalOf
@@ -855,10 +857,12 @@
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- if (DesktopModeStatus.isEnabled()) {
- return desktopModeTaskRepository.map(Lazy::get);
- }
- return Optional.empty();
+ return desktopModeTaskRepository.flatMap((lazy)-> {
+ if (DesktopModeStatus.isEnabled()) {
+ return Optional.of(lazy.get());
+ }
+ return Optional.empty();
+ });
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 0890861..e63bbc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -252,12 +252,7 @@
Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e);
}
nextFinishCallback.onTransitionFinished(null);
- } else if (nextInfo.getType() == TRANSIT_SLEEP) {
- // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep
- // token is held. In cases where keyguard is showing, we are running the animation for
- // the device sleeping/waking, so it's best to ignore this and keep playing anyway.
- return;
- } else if (handles(nextInfo)) {
+ } else {
// In all other cases, fast-forward to let the next queued transition start playing.
finishAnimationImmediately(currentTransition, playing);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 57cc28d..8eb4a5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -427,10 +427,10 @@
new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint));
}
- void setAppIconContentOverlay(Context context, Rect bounds, ActivityInfo activityInfo,
- int appIconSizePx) {
+ void setAppIconContentOverlay(Context context, Rect appBounds, Rect destinationBounds,
+ ActivityInfo activityInfo, int appIconSizePx) {
reattachContentOverlay(
- new PipContentOverlay.PipAppIconOverlay(context, bounds,
+ new PipContentOverlay.PipAppIconOverlay(context, appBounds, destinationBounds,
new IconProvider(context).getIcon(activityInfo), appIconSizePx));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index c701b95..850b06a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -180,20 +180,34 @@
private final Context mContext;
private final int mAppIconSizePx;
private final Rect mAppBounds;
+ private final int mOverlayHalfSize;
private final Matrix mTmpTransform = new Matrix();
private final float[] mTmpFloat9 = new float[9];
private Bitmap mBitmap;
- public PipAppIconOverlay(Context context, Rect appBounds,
+ public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
Drawable appIcon, int appIconSizePx) {
mContext = context;
final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
- mAppBounds = new Rect(appBounds);
- mBitmap = Bitmap.createBitmap(appBounds.width(), appBounds.height(),
- Bitmap.Config.ARGB_8888);
+
+ final int appWidth = appBounds.width();
+ final int appHeight = appBounds.height();
+
+ // In order to have the overlay always cover the pip window during the transition, the
+ // overlay will be drawn with the max size of the start and end bounds in different
+ // rotation.
+ final int overlaySize = Math.max(Math.max(appWidth, appHeight),
+ Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+ mOverlayHalfSize = overlaySize >> 1;
+
+ // When the activity is in the secondary split, make sure the scaling center is not
+ // offset.
+ mAppBounds = new Rect(0, 0, appWidth, appHeight);
+
+ mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
prepareAppIconOverlay(appIcon);
mLeash = new SurfaceControl.Builder(new SurfaceSession())
.setCallsite(TAG)
@@ -215,12 +229,19 @@
public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
Rect currentBounds, float fraction) {
mTmpTransform.reset();
+ // In order for the overlay to always cover the pip window, the overlay may have a
+ // size larger than the pip window. Make sure that app icon is at the center.
+ final int appBoundsCenterX = mAppBounds.centerX();
+ final int appBoundsCenterY = mAppBounds.centerY();
+ mTmpTransform.setTranslate(
+ appBoundsCenterX - mOverlayHalfSize,
+ appBoundsCenterY - mOverlayHalfSize);
// Scale back the bitmap with the pivot point at center.
mTmpTransform.postScale(
(float) mAppBounds.width() / currentBounds.width(),
(float) mAppBounds.height() / currentBounds.height(),
- mAppBounds.centerX(),
- mAppBounds.centerY());
+ appBoundsCenterX,
+ appBoundsCenterY);
atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
}
@@ -253,10 +274,10 @@
ta.recycle();
}
final Rect appIconBounds = new Rect(
- mAppBounds.centerX() - mAppIconSizePx / 2,
- mAppBounds.centerY() - mAppIconSizePx / 2,
- mAppBounds.centerX() + mAppIconSizePx / 2,
- mAppBounds.centerY() + mAppIconSizePx / 2);
+ mOverlayHalfSize - mAppIconSizePx / 2,
+ mOverlayHalfSize - mAppIconSizePx / 2,
+ mOverlayHalfSize + mAppIconSizePx / 2,
+ mOverlayHalfSize + mAppIconSizePx / 2);
appIcon.setBounds(appIconBounds);
appIcon.draw(canvas);
mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9e8f9c6..083cd08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1718,7 +1718,7 @@
sourceHintRect = computeRotatedBounds(rotationDelta, direction, destinationBounds,
sourceHintRect);
}
- Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
+ final Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null
&& mPipAnimationController.getCurrentAnimator().isRunning();
@@ -1741,7 +1741,7 @@
final boolean hasTopActivityInfo = mTaskInfo.topActivityInfo != null;
if (hasTopActivityInfo) {
animator.setAppIconContentOverlay(
- mContext, currentBounds, mTaskInfo.topActivityInfo,
+ mContext, currentBounds, destinationBounds, mTaskInfo.topActivityInfo,
mPipBoundsState.getLauncherState().getAppIconSizePx());
} else {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 018d674..d5b29e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -916,7 +916,7 @@
final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null;
if (hasTopActivityInfo) {
animator.setAppIconContentOverlay(
- mContext, currentBounds, taskInfo.topActivityInfo,
+ mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo,
mPipBoundsState.getLauncherState().getAppIconSizePx());
} else {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 1941d66..652a2ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -70,8 +70,8 @@
private int mMarginMenuStart;
private int mMenuHeight;
private int mMenuWidth;
-
private final int mCaptionHeight;
+ private HandleMenuAnimator mHandleMenuAnimator;
HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
@@ -111,20 +111,19 @@
mHandleMenuWindow = mParentDecor.addWindow(
R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
t, ssg, x, y, mMenuWidth, mMenuHeight);
+ final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView();
+ mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight);
}
/**
* Animates the appearance of the handle menu and its three pills.
*/
private void animateHandleMenu() {
- final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView();
- final HandleMenuAnimator handleMenuAnimator = new HandleMenuAnimator(handleMenuView,
- mMenuWidth, mCaptionHeight);
if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
|| mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
- handleMenuAnimator.animateCaptionHandleExpandToOpen();
+ mHandleMenuAnimator.animateCaptionHandleExpandToOpen();
} else {
- handleMenuAnimator.animateOpen();
+ mHandleMenuAnimator.animateOpen();
}
}
@@ -328,8 +327,16 @@
}
void close() {
- mHandleMenuWindow.releaseView();
- mHandleMenuWindow = null;
+ final Runnable after = () -> {
+ mHandleMenuWindow.releaseView();
+ mHandleMenuWindow = null;
+ };
+ if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+ mHandleMenuAnimator.animateCollapseIntoHandleClose(after);
+ } else {
+ mHandleMenuAnimator.animateClose(after);
+ }
}
static final class Builder {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
index 531de1f..8c5d4a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
@@ -26,6 +26,7 @@
import android.view.View.TRANSLATION_Y
import android.view.View.TRANSLATION_Z
import android.view.ViewGroup
+import androidx.core.animation.doOnEnd
import androidx.core.view.children
import com.android.wm.shell.R
import com.android.wm.shell.animation.Interpolators
@@ -37,27 +38,36 @@
private val captionHeight: Float
) {
companion object {
- private const val MENU_Y_TRANSLATION_DURATION: Long = 150
- private const val HEADER_NONFREEFORM_SCALE_DURATION: Long = 150
- private const val HEADER_FREEFORM_SCALE_DURATION: Long = 217
- private const val HEADER_ELEVATION_DURATION: Long = 83
- private const val HEADER_CONTENT_ALPHA_DURATION: Long = 100
- private const val BODY_SCALE_DURATION: Long = 180
- private const val BODY_ALPHA_DURATION: Long = 150
- private const val BODY_ELEVATION_DURATION: Long = 83
- private const val BODY_CONTENT_ALPHA_DURATION: Long = 167
+ // Open animation constants
+ private const val MENU_Y_TRANSLATION_OPEN_DURATION: Long = 150
+ private const val HEADER_NONFREEFORM_SCALE_OPEN_DURATION: Long = 150
+ private const val HEADER_FREEFORM_SCALE_OPEN_DURATION: Long = 217
+ private const val HEADER_ELEVATION_OPEN_DURATION: Long = 83
+ private const val HEADER_CONTENT_ALPHA_OPEN_DURATION: Long = 100
+ private const val BODY_SCALE_OPEN_DURATION: Long = 180
+ private const val BODY_ALPHA_OPEN_DURATION: Long = 150
+ private const val BODY_ELEVATION_OPEN_DURATION: Long = 83
+ private const val BODY_CONTENT_ALPHA_OPEN_DURATION: Long = 167
- private const val ELEVATION_DELAY: Long = 33
- private const val HEADER_CONTENT_ALPHA_DELAY: Long = 67
- private const val BODY_SCALE_DELAY: Long = 50
- private const val BODY_ALPHA_DELAY: Long = 133
+ private const val ELEVATION_OPEN_DELAY: Long = 33
+ private const val HEADER_CONTENT_ALPHA_OPEN_DELAY: Long = 67
+ private const val BODY_SCALE_OPEN_DELAY: Long = 50
+ private const val BODY_ALPHA_OPEN_DELAY: Long = 133
private const val HALF_INITIAL_SCALE: Float = 0.5f
private const val NONFREEFORM_HEADER_INITIAL_SCALE_X: Float = 0.6f
private const val NONFREEFORM_HEADER_INITIAL_SCALE_Y: Float = 0.05f
+
+ // Close animation constants
+ private const val HEADER_CLOSE_DELAY: Long = 20
+ private const val HEADER_CLOSE_DURATION: Long = 50
+ private const val HEADER_CONTENT_OPACITY_CLOSE_DELAY: Long = 25
+ private const val HEADER_CONTENT_OPACITY_CLOSE_DURATION: Long = 25
+ private const val BODY_CLOSE_DURATION: Long = 50
}
private val animators: MutableList<Animator> = mutableListOf()
+ private var runningAnimation: AnimatorSet? = null
private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill)
private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill)
@@ -67,9 +77,9 @@
fun animateOpen() {
prepareMenuForAnimation()
appInfoPillExpand()
- animateAppInfoPill()
- animateWindowingPill()
- animateMoreActionsPill()
+ animateAppInfoPillOpen()
+ animateWindowingPillOpen()
+ animateMoreActionsPillOpen()
runAnimations()
}
@@ -81,13 +91,44 @@
fun animateCaptionHandleExpandToOpen() {
prepareMenuForAnimation()
captionHandleExpandIntoAppInfoPill()
- animateAppInfoPill()
- animateWindowingPill()
- animateMoreActionsPill()
+ animateAppInfoPillOpen()
+ animateWindowingPillOpen()
+ animateMoreActionsPillOpen()
runAnimations()
}
/**
+ * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then,
+ * the app info pill will collapse into the shape of the caption handle in full screen and split
+ * screen.
+ *
+ * @param after runs after the animation finishes.
+ */
+ fun animateCollapseIntoHandleClose(after: Runnable) {
+ appInfoCollapseToHandle()
+ animateAppInfoPillFadeOut()
+ windowingPillClose()
+ moreActionsPillClose()
+ runAnimations(after)
+ }
+
+ /**
+ * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then,
+ * the app info pill will collapse into the shape of the caption handle in full screen and split
+ * screen.
+ *
+ * @param after runs after animation finishes.
+ *
+ */
+ fun animateClose(after: Runnable) {
+ appInfoPillCollapse()
+ animateAppInfoPillFadeOut()
+ windowingPillClose()
+ moreActionsPillClose()
+ runAnimations(after)
+ }
+
+ /**
* Prepares the handle menu for animation. Presets the opacity of necessary menu components.
* Presets pivots of handle menu and body pills for scaling animation.
*/
@@ -108,20 +149,20 @@
moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat()
}
- private fun animateAppInfoPill() {
+ private fun animateAppInfoPillOpen() {
// Header Elevation Animation
animators +=
ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Z, 1f).apply {
- startDelay = ELEVATION_DELAY
- duration = HEADER_ELEVATION_DURATION
+ startDelay = ELEVATION_OPEN_DELAY
+ duration = HEADER_ELEVATION_OPEN_DURATION
}
// Content Opacity Animation
appInfoPill.children.forEach {
animators +=
ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
- startDelay = HEADER_CONTENT_ALPHA_DELAY
- duration = HEADER_CONTENT_ALPHA_DURATION
+ startDelay = HEADER_CONTENT_ALPHA_OPEN_DELAY
+ duration = HEADER_CONTENT_ALPHA_OPEN_DURATION
}
}
}
@@ -130,17 +171,17 @@
// Header scaling animation
animators +=
ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X, 1f)
- .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }
+ .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION }
animators +=
ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y, 1f)
- .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION }
+ .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION }
// Downward y-translation animation
val yStart: Float = -captionHeight / 2
animators +=
ObjectAnimator.ofFloat(handleMenu, TRANSLATION_Y, yStart, 0f).apply {
- duration = MENU_Y_TRANSLATION_DURATION
+ duration = MENU_Y_TRANSLATION_OPEN_DURATION
}
}
@@ -148,98 +189,217 @@
// Header scaling animation
animators +=
ObjectAnimator.ofFloat(appInfoPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
- duration = HEADER_FREEFORM_SCALE_DURATION
+ duration = HEADER_FREEFORM_SCALE_OPEN_DURATION
}
animators +=
ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
- duration = HEADER_FREEFORM_SCALE_DURATION
+ duration = HEADER_FREEFORM_SCALE_OPEN_DURATION
}
}
- private fun animateWindowingPill() {
+ private fun animateWindowingPillOpen() {
// Windowing X & Y Scaling Animation
animators +=
ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
- startDelay = BODY_SCALE_DELAY
- duration = BODY_SCALE_DURATION
+ startDelay = BODY_SCALE_OPEN_DELAY
+ duration = BODY_SCALE_OPEN_DURATION
}
animators +=
ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
- startDelay = BODY_SCALE_DELAY
- duration = BODY_SCALE_DURATION
+ startDelay = BODY_SCALE_OPEN_DELAY
+ duration = BODY_SCALE_OPEN_DURATION
}
// Windowing Opacity Animation
animators +=
ObjectAnimator.ofFloat(windowingPill, ALPHA, 1f).apply {
- startDelay = BODY_ALPHA_DELAY
- duration = BODY_ALPHA_DURATION
+ startDelay = BODY_ALPHA_OPEN_DELAY
+ duration = BODY_ALPHA_OPEN_DURATION
}
// Windowing Elevation Animation
animators +=
ObjectAnimator.ofFloat(windowingPill, TRANSLATION_Z, 1f).apply {
- startDelay = ELEVATION_DELAY
- duration = BODY_ELEVATION_DURATION
+ startDelay = ELEVATION_OPEN_DELAY
+ duration = BODY_ELEVATION_OPEN_DURATION
}
// Windowing Content Opacity Animation
windowingPill.children.forEach {
animators +=
ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
- startDelay = BODY_ALPHA_DELAY
- duration = BODY_CONTENT_ALPHA_DURATION
+ startDelay = BODY_ALPHA_OPEN_DELAY
+ duration = BODY_CONTENT_ALPHA_OPEN_DURATION
interpolator = Interpolators.FAST_OUT_SLOW_IN
}
}
}
- private fun animateMoreActionsPill() {
+ private fun animateMoreActionsPillOpen() {
// More Actions X & Y Scaling Animation
animators +=
ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply {
- startDelay = BODY_SCALE_DELAY
- duration = BODY_SCALE_DURATION
+ startDelay = BODY_SCALE_OPEN_DELAY
+ duration = BODY_SCALE_OPEN_DURATION
}
animators +=
ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply {
- startDelay = BODY_SCALE_DELAY
- duration = BODY_SCALE_DURATION
+ startDelay = BODY_SCALE_OPEN_DELAY
+ duration = BODY_SCALE_OPEN_DURATION
}
// More Actions Opacity Animation
animators +=
ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 1f).apply {
- startDelay = BODY_ALPHA_DELAY
- duration = BODY_ALPHA_DURATION
+ startDelay = BODY_ALPHA_OPEN_DELAY
+ duration = BODY_ALPHA_OPEN_DURATION
}
// More Actions Elevation Animation
animators +=
ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Z, 1f).apply {
- startDelay = ELEVATION_DELAY
- duration = BODY_ELEVATION_DURATION
+ startDelay = ELEVATION_OPEN_DELAY
+ duration = BODY_ELEVATION_OPEN_DURATION
}
// More Actions Content Opacity Animation
moreActionsPill.children.forEach {
animators +=
ObjectAnimator.ofFloat(it, ALPHA, 1f).apply {
- startDelay = BODY_ALPHA_DELAY
- duration = BODY_CONTENT_ALPHA_DURATION
+ startDelay = BODY_ALPHA_OPEN_DELAY
+ duration = BODY_CONTENT_ALPHA_OPEN_DURATION
interpolator = Interpolators.FAST_OUT_SLOW_IN
}
}
}
- /** Runs the list of animators concurrently. */
- private fun runAnimations() {
- val animatorSet = AnimatorSet()
- animatorSet.playTogether(animators)
- animatorSet.start()
- animators.clear()
+ private fun appInfoPillCollapse() {
+ // Header scaling animation
+ animators +=
+ ObjectAnimator.ofFloat(appInfoPill, SCALE_X, 0f).apply {
+ startDelay = HEADER_CLOSE_DELAY
+ duration = HEADER_CLOSE_DURATION
+ }
+
+ animators +=
+ ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, 0f).apply {
+ startDelay = HEADER_CLOSE_DELAY
+ duration = HEADER_CLOSE_DURATION
+ }
+ }
+
+ private fun appInfoCollapseToHandle() {
+ // Header X & Y Scaling Animation
+ animators +=
+ ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X).apply {
+ startDelay = HEADER_CLOSE_DELAY
+ duration = HEADER_CLOSE_DURATION
+ }
+
+ animators +=
+ ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y).apply {
+ startDelay = HEADER_CLOSE_DELAY
+ duration = HEADER_CLOSE_DURATION
+ }
+ // Upward y-translation animation
+ val yStart: Float = -captionHeight / 2
+ animators +=
+ ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Y, yStart).apply {
+ startDelay = HEADER_CLOSE_DELAY
+ duration = HEADER_CLOSE_DURATION
+ }
+ }
+
+ private fun animateAppInfoPillFadeOut() {
+ // Header Content Opacity Animation
+ appInfoPill.children.forEach {
+ animators +=
+ ObjectAnimator.ofFloat(it, ALPHA, 0f).apply {
+ startDelay = HEADER_CONTENT_OPACITY_CLOSE_DELAY
+ duration = HEADER_CONTENT_OPACITY_CLOSE_DURATION
+ }
+ }
+ }
+
+ private fun windowingPillClose() {
+ // Windowing X & Y Scaling Animation
+ animators +=
+ ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ animators +=
+ ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ // windowing Animation
+ animators +=
+ ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ animators +=
+ ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+ }
+
+ private fun moreActionsPillClose() {
+ // More Actions X & Y Scaling Animation
+ animators +=
+ ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ animators +=
+ ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ // More Actions Opacity Animation
+ animators +=
+ ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ animators +=
+ ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+
+ // upward more actions pill y-translation animation
+ val yStart: Float = -captionHeight / 2
+ animators +=
+ ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Y, yStart).apply {
+ duration = BODY_CLOSE_DURATION
+ }
+ }
+
+ /**
+ * Runs the list of hide animators concurrently.
+ *
+ * @param after runs after animation finishes.
+ */
+ private fun runAnimations(after: Runnable? = null) {
+ runningAnimation?.apply {
+ // Remove all listeners, so that after runnable isn't triggered upon cancel.
+ removeAllListeners()
+ // If an animation runs while running animation is triggered, gracefully cancel.
+ cancel()
+ }
+
+ runningAnimation = AnimatorSet().apply {
+ playTogether(animators)
+ animators.clear()
+ doOnEnd {
+ after?.run()
+ runningAnimation = null
+ }
+ start()
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 386983c..4d11dfb 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -124,10 +124,6 @@
":WMShellFlickerTestsPipCommon-src",
],
static_libs: ["WMShellFlickerTestsBase"],
- test_suites: [
- "device-tests",
- "csuite",
- ],
}
csuite_test {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index ad600d0..6c3172a 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -46,7 +46,7 @@
#ifndef __ANDROID__ // Layoutlib does not compile HWUIProperties.sysprop as it depends on cutils properties
std::optional<bool> use_vulkan() {
- return base::GetBoolProperty("ro.hwui.use_vulkan", false);
+ return base::GetBoolProperty("ro.hwui.use_vulkan", true);
}
std::optional<std::int32_t> render_ahead() {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d28bb49..3e131bc 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -40,6 +40,7 @@
#ifdef __ANDROID__
#include "include/gpu/ganesh/SkImageGanesh.h"
#endif
+#include "utils/ForceDark.h"
#include "utils/MathUtils.h"
#include "utils/StringUtils.h"
@@ -403,16 +404,21 @@
deleteDisplayList(observer, info);
mDisplayList = std::move(mStagingDisplayList);
if (mDisplayList) {
- WebViewSyncData syncData {
- .applyForceDark = info && !info->disableForceDark
- };
+ WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info)};
mDisplayList.syncContents(syncData);
handleForceDark(info);
}
}
+inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) {
+ return CC_UNLIKELY(
+ info &&
+ (!info->disableForceDark ||
+ info->forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK));
+}
+
void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
- if (CC_LIKELY(!info || info->disableForceDark)) {
+ if (!shouldEnableForceDark(info)) {
return;
}
auto usage = usageHint();
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c959db3..1f3834be 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -233,6 +233,7 @@
void syncProperties();
void syncDisplayList(TreeObserver& observer, TreeInfo* info);
void handleForceDark(TreeInfo* info);
+ bool shouldEnableForceDark(TreeInfo* info);
void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
void pushStagingPropertiesChanges(TreeInfo& info);
diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp
index 750f869..717157c 100644
--- a/libs/hwui/TreeInfo.cpp
+++ b/libs/hwui/TreeInfo.cpp
@@ -24,7 +24,8 @@
: mode(mode)
, prepareTextures(mode == MODE_FULL)
, canvasContext(canvasContext)
- , disableForceDark(canvasContext.useForceDark() ? 0 : 1)
+ , disableForceDark(canvasContext.getForceDarkType() == ForceDarkType::NONE ? 1 : 0)
+ , forceDarkType(canvasContext.getForceDarkType())
, screenSize(canvasContext.getNextFrameSize()) {}
} // namespace android::uirenderer
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ea25f68..88449f3 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -24,6 +24,7 @@
#include "Properties.h"
#include "SkSize.h"
#include "SkippedFrameInfo.h"
+#include "utils/ForceDark.h"
#include "utils/Macros.h"
namespace android {
@@ -97,6 +98,7 @@
bool updateWindowPositions = false;
int disableForceDark;
+ ForceDarkType forceDarkType = ForceDarkType::NONE;
const SkISize screenSize;
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 422ffea..d15b1680 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -35,6 +35,7 @@
#include <gui/TraceUtils.h>
#include <include/encode/SkPngEncoder.h>
#include <inttypes.h>
+#include <log/log.h>
#include <media/NdkImage.h>
#include <media/NdkImageReader.h>
#include <nativehelper/JNIPlatformHelp.h>
@@ -53,11 +54,11 @@
#include <algorithm>
#include <atomic>
-#include <log/log.h>
#include <vector>
#include "JvmErrorReporter.h"
#include "android_graphics_HardwareRendererObserver.h"
+#include "utils/ForceDark.h"
namespace android {
@@ -824,10 +825,10 @@
proxy->allocateBuffers();
}
-static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz,
- jlong proxyPtr, jboolean enable) {
+static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, jlong proxyPtr,
+ jint type) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- proxy->setForceDark(enable);
+ proxy->setForceDark(static_cast<ForceDarkType>(type));
}
static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) {
@@ -1016,7 +1017,7 @@
{"nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess},
{"nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority},
{"nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers},
- {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
+ {"nSetForceDark", "(JI)V", (void*)android_view_ThreadedRenderer_setForceDark},
{"nSetDisplayDensityDpi", "(I)V",
(void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
{"nInitDisplayInfo", "(IIFIJJZZ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 7744786..c5ffbb7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -187,11 +187,12 @@
dumpResourceCacheUsage();
}
- return {true, IRenderPipeline::DrawResult::kUnknownTime};
+ return {true, IRenderPipeline::DrawResult::kUnknownTime, android::base::unique_fd{}};
}
-bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) {
+bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) {
GL_CHECKPOINT(LOW);
// Even if we decided to cancel the frame, from the perspective of jank
@@ -202,7 +203,7 @@
return false;
}
- *requireSwap = drew || mEglManager.damageRequiresSwap();
+ *requireSwap = drawResult.success || mEglManager.damageRequiresSwap();
if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
return false;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index f0461be..098a746 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -44,8 +44,9 @@
const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
const renderthread::HardwareBufferRenderParams& bufferParams) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; }
- bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
[[nodiscard]] android::base::unique_fd flush() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 86096d5..12cb69d 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -87,7 +87,7 @@
}
if (backBuffer.get() == nullptr) {
- return {false, -1};
+ return {false, -1, android::base::unique_fd{}};
}
// update the coordinates of the global light position based on surface rotation
@@ -110,10 +110,10 @@
profiler->draw(profileRenderer);
}
- nsecs_t submissionTime = IRenderPipeline::DrawResult::kUnknownTime;
+ VulkanManager::VkDrawResult drawResult;
{
ATRACE_NAME("flush commands");
- submissionTime = vulkanManager().finishFrame(backBuffer.get());
+ drawResult = vulkanManager().finishFrame(backBuffer.get());
}
layerUpdateQueue->clear();
@@ -122,11 +122,12 @@
dumpResourceCacheUsage();
}
- return {true, submissionTime};
+ return {true, drawResult.submissionTime, std::move(drawResult.presentFence)};
}
-bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) {
+bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) {
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
currentFrameInfo->markSwapBuffers();
@@ -135,10 +136,10 @@
return false;
}
- *requireSwap = drew;
+ *requireSwap = drawResult.success;
if (*requireSwap) {
- vulkanManager().swapBuffers(mVkSurface, screenDirty);
+ vulkanManager().swapBuffers(mVkSurface, screenDirty, std::move(drawResult.presentFence));
}
return *requireSwap;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 284cde5..e2ea57d 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -44,8 +44,9 @@
const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
const renderthread::HardwareBufferRenderParams& bufferParams) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; }
- bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
[[nodiscard]] android::base::unique_fd flush() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 7fac0c9..56b52dc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -664,8 +664,8 @@
bool didDraw = false;
int error = OK;
- bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult.success, windowDirty,
- mCurrentFrameInfo, &requireSwap);
+ bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty, mCurrentFrameInfo,
+ &requireSwap);
mCurrentFrameInfo->set(FrameInfoIndex::CommandSubmissionCompleted) = std::max(
drawResult.commandSubmissionTime, mCurrentFrameInfo->get(FrameInfoIndex::SwapBuffers));
@@ -969,6 +969,7 @@
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
+ ScopedActiveContext activeContext(this);
TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 37e4f7ec..be9b649 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -46,6 +46,7 @@
#include "renderstate/RenderState.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
+#include "utils/ForceDark.h"
#include "utils/RingBuffer.h"
namespace android {
@@ -194,11 +195,9 @@
mRenderPipeline->setPictureCapturedCallback(callback);
}
- void setForceDark(bool enable) { mUseForceDark = enable; }
+ void setForceDark(ForceDarkType type) { mForceDarkType = type; }
- bool useForceDark() {
- return mUseForceDark;
- }
+ ForceDarkType getForceDarkType() { return mForceDarkType; }
SkISize getNextFrameSize() const;
@@ -321,7 +320,7 @@
nsecs_t mLastDropVsync = 0;
bool mOpaque;
- bool mUseForceDark = false;
+ ForceDarkType mForceDarkType = ForceDarkType::NONE;
LightInfo mLightInfo;
LightGeometry mLightGeometry = {{0, 0, 0}, 0};
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 6c2cb9d..9c879d5 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -61,6 +61,7 @@
// submission occurred. -1 if this time is unknown.
static constexpr nsecs_t kUnknownTime = -1;
nsecs_t commandSubmissionTime = kUnknownTime;
+ android::base::unique_fd presentFence;
};
virtual DrawResult draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
@@ -68,8 +69,9 @@
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler,
const HardwareBufferRenderParams& bufferParams) = 0;
- virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
+ virtual bool swapBuffers(const Frame& frame, IRenderPipeline::DrawResult&,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
[[nodiscard]] virtual android::base::unique_fd flush() = 0;
virtual void setHardwareBuffer(AHardwareBuffer* hardwareBuffer) = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index be163ba..c3c136f 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -417,8 +417,8 @@
});
}
-void RenderProxy::setForceDark(bool enable) {
- mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
+void RenderProxy::setForceDark(ForceDarkType type) {
+ mRenderThread.queue().post([this, type]() { mContext->setForceDark(type); });
}
void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 47c1b0c..f2d8e94 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -31,6 +31,7 @@
#include "DrawFrameTask.h"
#include "SwapBehavior.h"
#include "hwui/Bitmap.h"
+#include "utils/ForceDark.h"
class SkBitmap;
class SkPicture;
@@ -142,7 +143,7 @@
void addFrameMetricsObserver(FrameMetricsObserver* observer);
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
- void setForceDark(bool enable);
+ void setForceDark(ForceDarkType type);
static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 22c5862..e706eb0 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -531,32 +531,21 @@
PFN_vkDestroySemaphore mDestroyFunction;
VkDevice mDevice;
VkSemaphore mSemaphore;
- // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia
- // (including by the GPU) and inside the VulkanManager. So we always start with two refs, one
- // owned by Skia and one owned by the VulkanManager. The refs are decremented each time
- // destroy_semaphore is called with this object. Skia will call destroy_semaphore once it is
- // done with the semaphore and the GPU has finished work on the semaphore. The VulkanManager
- // calls destroy_semaphore after sending the semaphore to Skia and exporting it if need be.
- int mRefs = 2;
DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
VkSemaphore semaphore)
: mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {}
+
+ ~DestroySemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
};
static void destroy_semaphore(void* context) {
DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(context);
- --info->mRefs;
- if (!info->mRefs) {
- info->mDestroyFunction(info->mDevice, info->mSemaphore, nullptr);
- delete info;
- }
+ delete info;
}
-nsecs_t VulkanManager::finishFrame(SkSurface* surface) {
+VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ATRACE_NAME("Vulkan finish frame");
- ALOGE_IF(mSwapSemaphore != VK_NULL_HANDLE || mDestroySemaphoreContext != nullptr,
- "finishFrame already has an outstanding semaphore");
VkExportSemaphoreCreateInfo exportInfo;
exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
@@ -576,11 +565,11 @@
GrFlushInfo flushInfo;
if (err == VK_SUCCESS) {
- mDestroySemaphoreContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
flushInfo.fNumSemaphores = 1;
flushInfo.fSignalSemaphores = &backendSemaphore;
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = mDestroySemaphoreContext;
+ flushInfo.fFinishedContext =
+ new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
} else {
semaphore = VK_NULL_HANDLE;
}
@@ -589,10 +578,11 @@
GrSemaphoresSubmitted submitted = context->flush(
surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
context->submit();
- const nsecs_t submissionTime = systemTime();
+ VkDrawResult drawResult{
+ .submissionTime = systemTime(),
+ };
if (semaphore != VK_NULL_HANDLE) {
if (submitted == GrSemaphoresSubmitted::kYes) {
- mSwapSemaphore = semaphore;
if (mFrameBoundaryANDROID) {
// retrieve VkImage used as render target
VkImage image = VK_NULL_HANDLE;
@@ -611,45 +601,37 @@
}
// frameBoundaryANDROID needs to know about mSwapSemaphore, but
// it won't wait on it.
- mFrameBoundaryANDROID(mDevice, mSwapSemaphore, image);
+ mFrameBoundaryANDROID(mDevice, semaphore, image);
}
- } else {
- destroy_semaphore(mDestroySemaphoreContext);
- mDestroySemaphoreContext = nullptr;
}
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ int fenceFd = -1;
+ err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
+ drawResult.presentFence.reset(fenceFd);
+ } else {
+ ALOGE("VulkanManager::finishFrame(): Semaphore submission failed");
+ mQueueWaitIdle(mGraphicsQueue);
}
+
skiapipeline::ShaderCache::get().onVkFrameFlushed(context);
- return submissionTime;
+ return drawResult;
}
-void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) {
+void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect,
+ android::base::unique_fd&& presentFence) {
if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
mDeviceWaitIdle(mDevice);
}
- int fenceFd = -1;
- if (mSwapSemaphore != VK_NULL_HANDLE) {
- VkSemaphoreGetFdInfoKHR getFdInfo;
- getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
- getFdInfo.pNext = nullptr;
- getFdInfo.semaphore = mSwapSemaphore;
- getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
- ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
- } else {
- ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed");
- mQueueWaitIdle(mGraphicsQueue);
- }
- if (mDestroySemaphoreContext) {
- destroy_semaphore(mDestroySemaphoreContext);
- }
-
- surface->presentCurrentBuffer(dirtyRect, fenceFd);
- mSwapSemaphore = VK_NULL_HANDLE;
- mDestroySemaphoreContext = nullptr;
+ surface->presentCurrentBuffer(dirtyRect, presentFence.release());
}
void VulkanManager::destroySurface(VulkanSurface* surface) {
@@ -753,22 +735,17 @@
GrBackendSemaphore backendSemaphore;
backendSemaphore.initVulkan(semaphore);
- DestroySemaphoreInfo* destroyInfo =
- new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
// Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback
- // which will remove its ref to the semaphore. The VulkanManager must still release its ref,
- // when it is done with the semaphore.
GrFlushInfo flushInfo;
flushInfo.fNumSemaphores = 1;
flushInfo.fSignalSemaphores = &backendSemaphore;
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = destroyInfo;
+ flushInfo.fFinishedContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
grContext->submit();
if (submitted == GrSemaphoresSubmitted::kNo) {
ALOGE("VulkanManager::createReleaseFence: Failed to submit semaphore");
- destroy_semaphore(destroyInfo);
return INVALID_OPERATION;
}
@@ -781,7 +758,6 @@
int fenceFd = 0;
err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
- destroy_semaphore(destroyInfo);
if (VK_SUCCESS != err) {
ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd");
return INVALID_OPERATION;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index dbef7fb..b92ebb3 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -22,6 +22,7 @@
#endif
#include <GrContextOptions.h>
#include <SkSurface.h>
+#include <android-base/unique_fd.h>
#include <utils/StrongPointer.h>
#include <vk/GrVkBackendContext.h>
#include <vk/GrVkExtensions.h>
@@ -82,10 +83,17 @@
void destroySurface(VulkanSurface* surface);
Frame dequeueNextBuffer(VulkanSurface* surface);
+
+ struct VkDrawResult {
+ // The estimated start time for intiating GPU work, -1 if unknown.
+ nsecs_t submissionTime;
+ android::base::unique_fd presentFence;
+ };
+
// Finishes the frame and submits work to the GPU
- // Returns the estimated start time for intiating GPU work, -1 otherwise.
- nsecs_t finishFrame(SkSurface* surface);
- void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect);
+ VkDrawResult finishFrame(SkSurface* surface);
+ void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect,
+ android::base::unique_fd&& presentFence);
// Inserts a wait on fence command into the Vulkan command buffer.
status_t fenceWait(int fence, GrDirectContext* grContext);
@@ -201,9 +209,6 @@
GrVkExtensions mExtensions;
uint32_t mDriverVersion = 0;
- VkSemaphore mSwapSemaphore = VK_NULL_HANDLE;
- void* mDestroySemaphoreContext = nullptr;
-
std::once_flag mInitFlag;
std::atomic_bool mInitialized = false;
};
diff --git a/libs/hwui/utils/ForceDark.h b/libs/hwui/utils/ForceDark.h
new file mode 100644
index 0000000..28538c4b
--- /dev/null
+++ b/libs/hwui/utils/ForceDark.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef FORCEDARKUTILS_H
+#define FORCEDARKUTILS_H
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * The type of force dark set on the renderer, if any.
+ *
+ * This should stay in sync with the java @IntDef in
+ * frameworks/base/graphics/java/android/graphics/ForceDarkType.java
+ */
+enum class ForceDarkType : __uint8_t { NONE = 0, FORCE_DARK = 1, FORCE_INVERT_COLOR_DARK = 2 };
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // FORCEDARKUTILS_H
\ No newline at end of file
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
index 1a9ebbc..d607126 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
@@ -17,6 +17,7 @@
package android.media.audiopolicy;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
@@ -107,7 +108,7 @@
}
@Override
- public boolean equals(@NonNull Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index 08a8d89..2769dbc 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -127,7 +127,7 @@
if (item->getTimestamp() > 0) {
bh.put(mediametrics::BUNDLE_TIMESTAMP, (int64_t)item->getTimestamp());
}
- if (item->getUid() != -1) {
+ if (static_cast<int32_t>(item->getUid()) != -1) {
bh.put(mediametrics::BUNDLE_UID, (int32_t)item->getUid());
}
for (const auto &prop : *item) {
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index c883b1f2..2a89a99 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -35,6 +35,7 @@
"androidx.compose.ui_ui",
"androidx.compose.ui_ui-tooling",
"androidx.core_core-ktx",
+ "androidx.hilt_hilt-navigation-compose",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-runtime-ktx",
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 0a63cb7..f2df64a 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -21,16 +21,10 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
import androidx.wear.compose.material.MaterialTheme
import com.android.credentialmanager.ui.WearApp
-import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
import com.google.android.horologist.annotations.ExperimentalHorologistApi
-import com.google.android.horologist.compose.layout.belowTimeTextPreview
import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.launch
@AndroidEntryPoint(ComponentActivity::class)
class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
@@ -42,50 +36,14 @@
super.onCreate(savedInstanceState)
setTheme(android.R.style.Theme_DeviceDefault)
-
- // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a
- // workaround. Once the issue is fixed, remove the "else" bracket and leave only the
- // contents of the "if" bracket.
- if (false) {
- setContent {
- MaterialTheme {
- WearApp(
- viewModel = viewModel,
- onCloseApp = ::finish,
- )
- }
- }
- } else {
- // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed
- lifecycleScope.launch {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.uiState.collect { uiState ->
- when (uiState) {
- CredentialSelectorUiState.Idle -> {
- // Don't display anything, assuming that there should be minimal latency
- // to parse the Credential Manager intent and define the state of the
- // app. If latency is big, then a "loading" screen should be displayed
- // to the user.
- }
-
- is CredentialSelectorUiState.Get -> {
- setContent {
- MaterialTheme {
- SinglePasswordScreen(
- columnState = belowTimeTextPreview(),
- onCloseApp = ::finish,
- )
- }
- }
- }
-
- else -> finish()
- }
- }
- }
+ setContent {
+ MaterialTheme {
+ WearApp(
+ viewModel = viewModel,
+ onCloseApp = ::finish,
+ )
}
}
-
viewModel.onNewIntent(intent)
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index 81a0672..c28df3e8 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -27,8 +27,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.credentialmanager.R
import com.android.credentialmanager.TAG
import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
@@ -47,7 +47,7 @@
columnState: ScalingLazyColumnState,
onCloseApp: () -> Unit,
modifier: Modifier = Modifier,
- viewModel: SinglePasswordScreenViewModel = viewModel(),
+ viewModel: SinglePasswordScreenViewModel = hiltViewModel(),
) {
viewModel.initialize()
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 6001155..2b5fcd8 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -24,8 +24,8 @@
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
-import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.ColorInt;
@@ -41,9 +41,9 @@
* This component is used as the main switch of the page
* to enable or disable the prefereces on the page.
*/
-public class MainSwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener {
+public class MainSwitchBar extends LinearLayout implements OnCheckedChangeListener {
- private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
+ private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>();
@ColorInt
private int mBackgroundColor;
@@ -51,8 +51,8 @@
private int mBackgroundActivatedColor;
protected TextView mTextView;
- protected Switch mSwitch;
- private View mFrameView;
+ protected CompoundButton mSwitch;
+ private final View mFrameView;
public MainSwitchBar(Context context) {
this(context, null);
@@ -84,8 +84,8 @@
setClickable(true);
mFrameView = findViewById(R.id.frame);
- mTextView = (TextView) findViewById(R.id.switch_text);
- mSwitch = (Switch) findViewById(android.R.id.switch_widget);
+ mTextView = findViewById(R.id.switch_text);
+ mSwitch = findViewById(android.R.id.switch_widget);
addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
if (mSwitch.getVisibility() == VISIBLE) {
@@ -136,13 +136,6 @@
}
/**
- * Return the Switch
- */
- public final Switch getSwitch() {
- return mSwitch;
- }
-
- /**
* Set the title text
*/
public void setTitle(CharSequence text) {
@@ -192,7 +185,7 @@
/**
* Adds a listener for switch changes
*/
- public void addOnSwitchChangeListener(OnMainSwitchChangeListener listener) {
+ public void addOnSwitchChangeListener(OnCheckedChangeListener listener) {
if (!mSwitchChangeListeners.contains(listener)) {
mSwitchChangeListeners.add(listener);
}
@@ -201,7 +194,7 @@
/**
* Remove a listener for switch changes
*/
- public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) {
+ public void removeOnSwitchChangeListener(OnCheckedChangeListener listener) {
mSwitchChangeListeners.remove(listener);
}
@@ -223,9 +216,8 @@
private void propagateChecked(boolean isChecked) {
setBackground(isChecked);
- final int count = mSwitchChangeListeners.size();
- for (int n = 0; n < count; n++) {
- mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked);
+ for (OnCheckedChangeListener changeListener : mSwitchChangeListeners) {
+ changeListener.onCheckedChanged(mSwitch, isChecked);
}
}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index 11a6804..b294d4e 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -19,24 +19,25 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
-import android.widget.Switch;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
+import com.android.settingslib.widget.mainswitch.R;
+
import java.util.ArrayList;
import java.util.List;
-import com.android.settingslib.widget.mainswitch.R;
-
/**
* MainSwitchPreference is a Preference with a customized Switch.
* This component is used as the main switch of the page
* to enable or disable the prefereces on the page.
*/
-public class MainSwitchPreference extends TwoStatePreference implements OnMainSwitchChangeListener {
+public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener {
- private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>();
+ private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>();
private MainSwitchBar mMainSwitchBar;
@@ -120,7 +121,7 @@
}
@Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
super.setChecked(isChecked);
}
@@ -138,7 +139,7 @@
/**
* Adds a listener for switch changes
*/
- public void addOnSwitchChangeListener(OnMainSwitchChangeListener listener) {
+ public void addOnSwitchChangeListener(OnCheckedChangeListener listener) {
if (!mSwitchChangeListeners.contains(listener)) {
mSwitchChangeListeners.add(listener);
}
@@ -151,7 +152,7 @@
/**
* Remove a listener for switch changes
*/
- public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) {
+ public void removeOnSwitchChangeListener(OnCheckedChangeListener listener) {
mSwitchChangeListeners.remove(listener);
if (mMainSwitchBar != null) {
mMainSwitchBar.removeOnSwitchChangeListener(listener);
@@ -159,7 +160,7 @@
}
private void registerListenerToSwitchBar() {
- for (OnMainSwitchChangeListener listener : mSwitchChangeListeners) {
+ for (OnCheckedChangeListener listener : mSwitchChangeListeners) {
mMainSwitchBar.addOnSwitchChangeListener(listener);
}
}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java
deleted file mode 100644
index 03868f9..0000000
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/OnMainSwitchChangeListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.widget;
-
-import android.widget.Switch;
-
-import com.android.settingslib.widget.mainswitch.R;
-
-/**
- * Called when the checked state of the Switch has changed.
- */
-public interface OnMainSwitchChangeListener {
- /**
- * @param switchView The Switch view whose state has changed.
- * @param isChecked The new checked state of switchView.
- */
- void onSwitchChanged(Switch switchView, boolean isChecked);
-}
diff --git a/packages/SettingsLib/Spa/gallery/res/values/strings.xml b/packages/SettingsLib/Spa/gallery/res/values/strings.xml
index ec60f8c..18a6db0 100644
--- a/packages/SettingsLib/Spa/gallery/res/values/strings.xml
+++ b/packages/SettingsLib/Spa/gallery/res/values/strings.xml
@@ -26,4 +26,9 @@
<string name="single_line_summary_preference_summary" translatable="false">A very long summary to show case a preference which only shows a single line summary.</string>
<!-- Footer text with two links. [DO NOT TRANSLATE] -->
<string name="footer_with_two_links" translatable="false">Annotated string with <a href="https://www.android.com/">link 1</a> and <a href="https://source.android.com/">link 2</a>.</string>
+
+ <!-- Sample title -->
+ <string name="sample_title" translatable="false">Lorem ipsum</string>
+ <!-- Sample text -->
+ <string name="sample_text" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent a rhoncus tellus. Nulla facilisi. Pellentesque erat ex, maximus viae turpis</string>
</resources>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index d62b490..b1e1585 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -22,6 +22,7 @@
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
@@ -98,6 +99,7 @@
SettingsExposedDropdownMenuCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
+ CardPageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
new file mode 100644
index 0000000..8386bc1
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.spa.gallery.card
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Error
+import androidx.compose.material.icons.outlined.PowerOff
+import androidx.compose.material.icons.outlined.Shield
+import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.widget.card.CardButton
+import com.android.settingslib.spa.widget.card.CardModel
+import com.android.settingslib.spa.widget.card.SettingsCard
+import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+object CardPageProvider : SettingsPageProvider {
+ override val name = "CardPage"
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SettingsCardWithIcon()
+ SettingsCardWithoutIcon()
+ SampleSettingsCollapsibleCard()
+ }
+ }
+
+ @Composable
+ private fun SettingsCardWithIcon() {
+ SettingsCard(
+ CardModel(
+ title = stringResource(R.string.sample_title),
+ text = stringResource(R.string.sample_text),
+ imageVector = Icons.Outlined.WarningAmber,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ CardButton(text = "Action", isMain = true) {},
+ )
+ )
+ )
+ }
+
+ @Composable
+ private fun SettingsCardWithoutIcon() {
+ SettingsCard(
+ CardModel(
+ title = stringResource(R.string.sample_title),
+ text = stringResource(R.string.sample_text),
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ ),
+ )
+ )
+ }
+
+ @Composable
+ fun SampleSettingsCollapsibleCard() {
+ SettingsCollapsibleCard(
+ title = "More alerts",
+ imageVector = Icons.Outlined.Error,
+ models = listOf(
+ CardModel(
+ title = stringResource(R.string.sample_title),
+ text = stringResource(R.string.sample_text),
+ imageVector = Icons.Outlined.PowerOff,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ )
+ ),
+ CardModel(
+ title = stringResource(R.string.sample_title),
+ text = stringResource(R.string.sample_text),
+ imageVector = Icons.Outlined.Shield,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ CardButton(text = "Main action", isMain = true) {},
+ )
+ )
+ )
+ )
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+
+ private const val TITLE = "Sample Card"
+}
+
+@Preview
+@Composable
+private fun CardPagePreview() {
+ SettingsTheme {
+ CardPageProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index b339b44..f52ceec 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -28,6 +28,7 @@
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
@@ -69,6 +70,7 @@
ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/UiModePreviews.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/UiModePreviews.kt
new file mode 100644
index 0000000..d48e564
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/UiModePreviews.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.spa.debug
+
+import android.content.res.Configuration
+import androidx.compose.ui.tooling.preview.Preview
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+annotation class UiModePreviews
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 4088ffd..47660bc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -20,6 +20,8 @@
import androidx.compose.ui.unit.dp
object SettingsDimension {
+ val paddingSmall = 4.dp
+
val itemIconSize = 24.dp
val itemIconContainerSize = 72.dp
val itemPaddingStart = 24.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
new file mode 100644
index 0000000..c113f43
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -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.
+ */
+
+package com.android.settingslib.spa.widget.card
+
+import androidx.compose.ui.graphics.vector.ImageVector
+
+data class CardButton(
+ val text: String,
+ val isMain: Boolean = false,
+ val onClick: () -> Unit,
+)
+
+data class CardModel(
+ val title: String,
+ val text: String,
+ val imageVector: ImageVector? = null,
+ val buttons: List<CardButton> = emptyList(),
+)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
new file mode 100644
index 0000000..10e2686
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.spa.widget.card
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.debug.UiModePreviews
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraLarge
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+
+@Composable
+fun SettingsCard(content: @Composable ColumnScope.() -> Unit) {
+ Card(
+ shape = CornerExtraLarge,
+ colors = CardDefaults.cardColors(
+ containerColor = SettingsTheme.colorScheme.surface,
+ ),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = SettingsDimension.itemPaddingEnd,
+ vertical = SettingsDimension.itemPaddingAround,
+ ),
+ content = content,
+ )
+}
+
+@Composable
+fun SettingsCard(model: CardModel) {
+ SettingsCard {
+ SettingsCardImpl(model)
+ }
+}
+
+@Composable
+internal fun SettingsCardImpl(model: CardModel) {
+ Column(
+ modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+ verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
+ ) {
+ CardIcon(model.imageVector)
+ SettingsTitle(model.title)
+ SettingsBody(model.text)
+ Buttons(model.buttons)
+ }
+}
+
+@Composable
+private fun CardIcon(imageVector: ImageVector?) {
+ if (imageVector != null) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ }
+}
+
+@Composable
+private fun Buttons(buttons: List<CardButton>) {
+ if (buttons.isNotEmpty()) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = SettingsDimension.itemPaddingAround),
+ horizontalArrangement = Arrangement.spacedBy(
+ space = SettingsDimension.itemPaddingEnd,
+ alignment = Alignment.End,
+ ),
+ ) {
+ for (button in buttons) {
+ Button(button)
+ }
+ }
+ }
+}
+
+@Composable
+private fun Button(button: CardButton) {
+ if (button.isMain) {
+ Button(
+ onClick = button.onClick,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = SettingsTheme.colorScheme.primaryContainer,
+ ),
+ ) {
+ Text(
+ text = button.text,
+ color = SettingsTheme.colorScheme.onPrimaryContainer,
+ )
+ }
+ } else {
+ OutlinedButton(onClick = button.onClick) {
+ Text(
+ text = button.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+ }
+}
+
+@UiModePreviews
+@Composable
+private fun SettingsCardPreview() {
+ SettingsTheme {
+ SettingsCard(
+ CardModel(
+ title = "Lorem ipsum",
+ text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ imageVector = Icons.Outlined.WarningAmber,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ CardButton(text = "Action", isMain = true) {},
+ )
+ )
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
new file mode 100644
index 0000000..7d10645
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.spa.widget.card
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Error
+import androidx.compose.material.icons.outlined.PowerOff
+import androidx.compose.material.icons.outlined.Shield
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.debug.UiModePreviews
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.ExpandIcon
+import com.android.settingslib.spa.widget.ui.SettingsDialogItem
+import com.android.settingslib.spa.widget.ui.SettingsTitleSmall
+
+@Composable
+fun SettingsCollapsibleCard(
+ title: String,
+ imageVector: ImageVector,
+ models: List<CardModel>
+) {
+ var expanded by rememberSaveable { mutableStateOf(false) }
+ SettingsCard {
+ Header(title, imageVector, models.size, expanded) { expanded = it }
+ AnimatedVisibility(expanded) {
+ Column {
+ for (model in models) {
+ HorizontalDivider(
+ thickness = SettingsDimension.paddingSmall,
+ color = MaterialTheme.colorScheme.surface,
+ )
+ SettingsCardImpl(model)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun Header(
+ title: String,
+ imageVector: ImageVector,
+ cardCount: Int,
+ expanded: Boolean,
+ setExpanded: (Boolean) -> Unit,
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { setExpanded(!expanded) }
+ .padding(
+ horizontal = SettingsDimension.itemPaddingStart,
+ vertical = SettingsDimension.itemPaddingVertical,
+ ),
+ horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingStart),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ Box(modifier = Modifier.weight(1f)) {
+ SettingsTitleSmall(title, useMediumWeight = true)
+ }
+ CardCount(cardCount, expanded)
+ }
+}
+
+@Composable
+private fun CardCount(modelSize: Int, expanded: Boolean) {
+ Surface(
+ shape = SettingsShape.CornerExtraLarge,
+ color = MaterialTheme.colorScheme.secondaryContainer,
+ ) {
+ Row(
+ modifier = Modifier.padding(SettingsDimension.paddingSmall),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Spacer(modifier = Modifier.padding(SettingsDimension.paddingSmall))
+ SettingsDialogItem(modelSize.toString())
+ ExpandIcon(expanded)
+ }
+ }
+}
+
+@UiModePreviews
+@Composable
+private fun SettingsCollapsibleCardPreview() {
+ SettingsTheme {
+ SettingsCollapsibleCard(
+ title = "More alerts",
+ imageVector = Icons.Outlined.Error,
+ models = listOf(
+ CardModel(
+ title = "Lorem ipsum",
+ text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ imageVector = Icons.Outlined.PowerOff,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ )
+ ),
+ CardModel(
+ title = "Lorem ipsum",
+ text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+ imageVector = Icons.Outlined.Shield,
+ buttons = listOf(
+ CardButton(text = "Action") {},
+ CardButton(text = "Main action", isMain = true) {},
+ )
+ )
+ )
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index 8cbf7cc..a9974dc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -76,13 +76,7 @@
contentPadding = contentPadding,
) {
SpinnerText(options.find { it.id == selectedId })
- Icon(
- imageVector = when {
- expanded -> Icons.Outlined.ExpandLess
- else -> Icons.Outlined.ExpandMore
- },
- contentDescription = null,
- )
+ ExpandIcon(expanded)
}
DropdownMenu(
expanded = expanded,
@@ -110,6 +104,17 @@
}
@Composable
+internal fun ExpandIcon(expanded: Boolean) {
+ Icon(
+ imageVector = when {
+ expanded -> Icons.Outlined.ExpandLess
+ else -> Icons.Outlined.ExpandMore
+ },
+ contentDescription = null,
+ )
+}
+
+@Composable
private fun SpinnerText(
option: SpinnerOption?,
modifier: Modifier = Modifier,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 7f1acff..f4b2843 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -26,6 +26,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -39,12 +40,16 @@
Text(
text = title,
color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.titleMedium.let {
- when (useMediumWeight) {
- true -> it.toMediumWeight()
- else -> it
- }
- },
+ style = MaterialTheme.typography.titleMedium.withWeight(useMediumWeight),
+ )
+}
+
+@Composable
+fun SettingsTitleSmall(title: String, useMediumWeight: Boolean = false) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.titleSmall.withWeight(useMediumWeight),
)
}
@@ -78,7 +83,9 @@
@Composable
fun PlaceholderTitle(title: String) {
Box(
- modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPadding),
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(SettingsDimension.itemPadding),
contentAlignment = Alignment.Center,
) {
Text(
@@ -89,6 +96,11 @@
}
}
+private fun TextStyle.withWeight(useMediumWeight: Boolean = false) = when (useMediumWeight) {
+ true -> toMediumWeight()
+ else -> this
+}
+
@Preview
@Composable
private fun BasePreferencePreview() {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
new file mode 100644
index 0000000..fd3ae49
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -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 com.android.settingslib.spa.widget.card
+
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsCardTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun settingsCard_titleDisplayed() {
+ composeTestRule.setContent {
+ SettingsCard(
+ CardModel(
+ title = TITLE,
+ text = "",
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCard_textDisplayed() {
+ composeTestRule.setContent {
+ SettingsCard(
+ CardModel(
+ title = "",
+ text = TEXT,
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCard_buttonDisplayed() {
+ composeTestRule.setContent {
+ SettingsCard(
+ CardModel(
+ title = "",
+ text = "",
+ buttons = listOf(
+ CardButton(text = TEXT) {}
+ ),
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCard_buttonCanBeClicked() {
+ var buttonClicked = false
+ composeTestRule.setContent {
+ SettingsCard(
+ CardModel(
+ title = "",
+ text = "",
+ buttons = listOf(
+ CardButton(text = TEXT) { buttonClicked = true }
+ ),
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithText(TEXT).performClick()
+
+ assertThat(buttonClicked).isTrue()
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val TEXT = "Text"
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
new file mode 100644
index 0000000..efe1c70
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.spa.widget.card
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Error
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsCollapsibleCardTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun settingsCollapsibleCard_titleDisplayed() {
+ setContent()
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCollapsibleCard_cardCountDisplayed() {
+ setContent()
+
+ composeTestRule.onNodeWithText("1").assertIsDisplayed()
+ }
+
+ @Test
+ fun settingsCollapsibleCard_initial_cardTextNotExists() {
+ setContent()
+
+ composeTestRule.onNodeWithText(CARD_TEXT).assertDoesNotExist()
+ }
+
+ @Test
+ fun settingsCollapsibleCard_afterExpand_cardTextDisplayed() {
+ setContent()
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onNodeWithText(CARD_TEXT).assertIsDisplayed()
+ }
+
+ private fun setContent() {
+ composeTestRule.setContent {
+ SettingsCollapsibleCard(
+ title = TITLE,
+ imageVector = Icons.Outlined.Error,
+ models = listOf(
+ CardModel(
+ title = "",
+ text = CARD_TEXT,
+ )
+ ),
+ )
+ }
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val CARD_TEXT = "Card Text"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlow.kt
new file mode 100644
index 0000000..2c60db4
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlow.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.settingslib.spaprivileged.framework.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * A [BroadcastReceiver] flow for the given [intentFilter].
+ */
+fun Context.broadcastReceiverAsUserFlow(
+ intentFilter: IntentFilter,
+ userHandle: UserHandle,
+): Flow<Intent> = callbackFlow {
+ val broadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ trySend(intent)
+ }
+ }
+ registerReceiverAsUser(
+ broadcastReceiver,
+ userHandle,
+ intentFilter,
+ null,
+ null,
+ Context.RECEIVER_NOT_EXPORTED,
+ )
+
+ awaitClose { unregisterReceiver(broadcastReceiver) }
+}.conflate().flowOn(Dispatchers.Default)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
index ad907cf..7d6ee19 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
@@ -17,14 +17,14 @@
package com.android.settingslib.spaprivileged.framework.compose
import android.content.BroadcastReceiver
-import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.UserHandle
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
-import com.android.settingslib.spa.framework.compose.LifecycleEffect
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
+import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverAsUserFlow
/**
* A [BroadcastReceiver] which registered when on start and unregistered when on stop.
@@ -35,27 +35,6 @@
userHandle: UserHandle,
onReceive: (Intent) -> Unit,
) {
- val context = LocalContext.current
- val broadcastReceiver = remember {
- object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- onReceive(intent)
- }
- }
- }
- LifecycleEffect(
- onStart = {
- context.registerReceiverAsUser(
- broadcastReceiver,
- userHandle,
- intentFilter,
- null,
- null,
- Context.RECEIVER_NOT_EXPORTED,
- )
- },
- onStop = {
- context.unregisterReceiver(broadcastReceiver)
- },
- )
+ LocalContext.current.broadcastReceiverAsUserFlow(intentFilter, userHandle)
+ .collectLatestWithLifecycle(LocalLifecycleOwner.current, action = onReceive)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt
index 8e702ea..8e28bf8 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt
@@ -23,21 +23,24 @@
import kotlin.reflect.KProperty
import kotlinx.coroutines.flow.Flow
-fun Context.settingsGlobalBoolean(name: String): ReadWriteProperty<Any?, Boolean> =
- SettingsGlobalBooleanDelegate(this, name)
+fun Context.settingsGlobalBoolean(name: String, defaultValue: Boolean = false):
+ ReadWriteProperty<Any?, Boolean> = SettingsGlobalBooleanDelegate(this, name, defaultValue)
-fun Context.settingsGlobalBooleanFlow(name: String): Flow<Boolean> {
- val value by settingsGlobalBoolean(name)
+fun Context.settingsGlobalBooleanFlow(name: String, defaultValue: Boolean = false): Flow<Boolean> {
+ val value by settingsGlobalBoolean(name, defaultValue)
return settingsGlobalFlow(name) { value }
}
-private class SettingsGlobalBooleanDelegate(context: Context, private val name: String) :
- ReadWriteProperty<Any?, Boolean> {
+private class SettingsGlobalBooleanDelegate(
+ context: Context,
+ private val name: String,
+ private val defaultValue: Boolean = false,
+) : ReadWriteProperty<Any?, Boolean> {
private val contentResolver: ContentResolver = context.contentResolver
override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean =
- Settings.Global.getInt(contentResolver, name, 0) != 0
+ Settings.Global.getInt(contentResolver, name, if (defaultValue) 1 else 0) != 0
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
Settings.Global.putInt(contentResolver, name, if (value) 1 else 0)
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlowTest.kt
new file mode 100644
index 0000000..dfb8e22
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BroadcastReceiverAsUserFlowTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.framework.common
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class BroadcastReceiverAsUserFlowTest {
+
+ private var registeredBroadcastReceiver: BroadcastReceiver? = null
+
+ private val context = mock<Context> {
+ on {
+ registerReceiverAsUser(
+ any(),
+ eq(USER_HANDLE),
+ eq(INTENT_FILTER),
+ isNull(),
+ isNull(),
+ eq(Context.RECEIVER_NOT_EXPORTED),
+ )
+ } doAnswer {
+ registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
+ null
+ }
+ }
+
+ @Test
+ fun broadcastReceiverAsUserFlow_registered() = runBlocking {
+ val flow = context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE)
+
+ flow.firstWithTimeoutOrNull()
+
+ assertThat(registeredBroadcastReceiver).isNotNull()
+ }
+
+ @Test
+ fun broadcastReceiverAsUserFlow_isCalledOnReceive() = runBlocking {
+ var onReceiveIsCalled = false
+ launch {
+ context.broadcastReceiverAsUserFlow(INTENT_FILTER, USER_HANDLE).first {
+ onReceiveIsCalled = true
+ true
+ }
+ }
+
+ delay(100)
+ registeredBroadcastReceiver!!.onReceive(context, Intent())
+ delay(100)
+
+ assertThat(onReceiveIsCalled).isTrue()
+ }
+
+ private companion object {
+ val USER_HANDLE: UserHandle = UserHandle.of(0)
+
+ val INTENT_FILTER = IntentFilter()
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
index 2c8fb66..f812f95 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
@@ -23,38 +23,32 @@
import android.os.UserHandle
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
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.any
+import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.eq
import org.mockito.kotlin.isNull
-import org.mockito.kotlin.whenever
+import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class DisposableBroadcastReceiverAsUserTest {
@get:Rule
val composeTestRule = createComposeRule()
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
-
- @Mock
- private lateinit var context: Context
-
private var registeredBroadcastReceiver: BroadcastReceiver? = null
- @Before
- fun setUp() {
- whenever(
- context.registerReceiverAsUser(
+ private val context = mock<Context> {
+ on {
+ registerReceiverAsUser(
any(),
eq(USER_HANDLE),
eq(INTENT_FILTER),
@@ -62,7 +56,7 @@
isNull(),
eq(Context.RECEIVER_NOT_EXPORTED),
)
- ).then {
+ } doAnswer {
registeredBroadcastReceiver = it.arguments[0] as BroadcastReceiver
null
}
@@ -71,7 +65,10 @@
@Test
fun broadcastReceiver_registered() {
composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
+ CompositionLocalProvider(
+ LocalContext provides context,
+ LocalLifecycleOwner provides TestLifecycleOwner(),
+ ) {
DisposableBroadcastReceiverAsUser(INTENT_FILTER, USER_HANDLE) {}
}
}
@@ -80,10 +77,13 @@
}
@Test
- fun broadcastReceiver_isCalledOnReceive() {
+ fun broadcastReceiver_isCalledOnReceive() = runBlocking {
var onReceiveIsCalled = false
composeTestRule.setContent {
- CompositionLocalProvider(LocalContext provides context) {
+ CompositionLocalProvider(
+ LocalContext provides context,
+ LocalLifecycleOwner provides TestLifecycleOwner(),
+ ) {
DisposableBroadcastReceiverAsUser(INTENT_FILTER, USER_HANDLE) {
onReceiveIsCalled = true
}
@@ -91,6 +91,7 @@
}
registeredBroadcastReceiver!!.onReceive(context, Intent())
+ delay(100)
assertThat(onReceiveIsCalled).isTrue()
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index 840bca8..44973a7 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -19,6 +19,8 @@
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
+import android.content.pm.FakeFeatureFlagsImpl
+import android.content.pm.Flags
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.PackageManager.ResolveInfoFlags
@@ -29,76 +31,62 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.R
-import com.android.settingslib.spaprivileged.framework.common.userManager
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
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.argumentCaptor
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-import android.content.pm.FakeFeatureFlagsImpl
-import android.content.pm.Flags
@RunWith(AndroidJUnit4::class)
class AppListRepositoryTest {
- @get:Rule
- val mockito: MockitoRule = MockitoJUnit.rule()
+ private val resources = mock<Resources> {
+ on { getStringArray(R.array.config_hideWhenDisabled_packageNames) } doReturn emptyArray()
+ }
- @Spy
- private val context: Context = ApplicationProvider.getApplicationContext()
-
- @Mock
- private lateinit var resources: Resources
-
- @Mock
- private lateinit var packageManager: PackageManager
-
- @Mock
- private lateinit var userManager: UserManager
-
- private lateinit var repository: AppListRepository
-
- @Before
- fun setUp() {
- whenever(context.resources).thenReturn(resources)
- whenever(resources.getStringArray(R.array.config_hideWhenDisabled_packageNames))
- .thenReturn(emptyArray())
- whenever(context.packageManager).thenReturn(packageManager)
- whenever(context.userManager).thenReturn(userManager)
- whenever(packageManager.getInstalledModules(any())).thenReturn(emptyList())
- whenever(packageManager.getHomeActivities(any())).thenAnswer {
+ private val packageManager = mock<PackageManager> {
+ on { getInstalledModules(any()) } doReturn emptyList()
+ on { getHomeActivities(any()) } doAnswer {
@Suppress("UNCHECKED_CAST")
val resolveInfos = it.arguments[0] as MutableList<ResolveInfo>
resolveInfos += resolveInfoOf(packageName = HOME_APP.packageName)
null
}
- whenever(
- packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), any<Int>())
- ).thenReturn(listOf(resolveInfoOf(packageName = IN_LAUNCHER_APP.packageName)))
- whenever(userManager.getUserInfo(ADMIN_USER_ID)).thenReturn(UserInfo().apply {
- flags = UserInfo.FLAG_ADMIN
- })
- whenever(userManager.getProfileIdsWithDisabled(ADMIN_USER_ID))
- .thenReturn(intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID))
-
- repository = AppListRepositoryImpl(context)
+ on { queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), any<Int>()) } doReturn
+ listOf(resolveInfoOf(packageName = IN_LAUNCHER_APP.packageName))
}
+ private val mockUserManager = mock<UserManager> {
+ on { getUserInfo(ADMIN_USER_ID) } doReturn UserInfo().apply {
+ flags = UserInfo.FLAG_ADMIN
+ }
+ on { getProfileIdsWithDisabled(ADMIN_USER_ID) } doReturn
+ intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID)
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { resources } doReturn resources
+ on { packageManager } doReturn packageManager
+ on { getSystemService(UserManager::class.java) } doReturn mockUserManager
+ }
+
+ private val repository = AppListRepositoryImpl(context)
+
private fun mockInstalledApplications(apps: List<ApplicationInfo>, userId: Int) {
- whenever(
- packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId))
- ).thenReturn(apps)
+ packageManager.stub {
+ on { getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(userId)) } doReturn
+ apps
+ }
}
@Test
@@ -135,13 +123,13 @@
)
assertThat(appList).containsExactly(NORMAL_APP)
- argumentCaptor<ApplicationInfoFlags> {
+ val flags = argumentCaptor<ApplicationInfoFlags> {
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
- assertThat(firstValue.value).isEqualTo(
- PackageManager.MATCH_DISABLED_COMPONENTS or
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- )
- }
+ }.firstValue
+ assertThat(flags.value).isEqualTo(
+ PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ )
}
@Test
@@ -154,11 +142,10 @@
)
assertThat(appList).containsExactly(NORMAL_APP)
- argumentCaptor<ApplicationInfoFlags> {
+ val flags = argumentCaptor<ApplicationInfoFlags> {
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
- assertThat(firstValue.value and PackageManager.MATCH_ANY_USER.toLong())
- .isGreaterThan(0L)
- }
+ }.firstValue
+ assertThat(flags.value and PackageManager.MATCH_ANY_USER.toLong()).isGreaterThan(0L)
}
@Test
@@ -278,14 +265,14 @@
val appList = repository.loadApps(userId = ADMIN_USER_ID)
assertThat(appList).containsExactly(NORMAL_APP, ARCHIVED_APP)
- argumentCaptor<ApplicationInfoFlags> {
+ val flags = argumentCaptor<ApplicationInfoFlags> {
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
- assertThat(firstValue.value).isEqualTo(
- (PackageManager.MATCH_DISABLED_COMPONENTS or
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() or
- PackageManager.MATCH_ARCHIVED_PACKAGES
- )
- }
+ }.firstValue
+ assertThat(flags.value).isEqualTo(
+ (PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong() or
+ PackageManager.MATCH_ARCHIVED_PACKAGES
+ )
}
@Test
@@ -294,13 +281,13 @@
val appList = repository.loadApps(userId = ADMIN_USER_ID)
assertThat(appList).containsExactly(NORMAL_APP)
- argumentCaptor<ApplicationInfoFlags> {
+ val flags = argumentCaptor<ApplicationInfoFlags> {
verify(packageManager).getInstalledApplicationsAsUser(capture(), eq(ADMIN_USER_ID))
- assertThat(firstValue.value).isEqualTo(
- PackageManager.MATCH_DISABLED_COMPONENTS or
- PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- )
- }
+ }.firstValue
+ assertThat(flags.value).isEqualTo(
+ PackageManager.MATCH_DISABLED_COMPONENTS or
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ )
}
@Test
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f074106..f03263b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -23,6 +23,7 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
+import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
@@ -704,12 +705,23 @@
continue;
}
for (int complianceWarningType : complianceWarnings) {
- switch (complianceWarningType) {
- case UsbPortStatus.COMPLIANCE_WARNING_OTHER:
- case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
- return true;
- default:
- break;
+ if (Flags.enableUsbDataComplianceWarning()
+ && Flags.enableInputPowerLimitedWarning()) {
+ switch (complianceWarningType) {
+ case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED:
+ case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
+ return true;
+ default:
+ break;
+ }
+ } else {
+ switch (complianceWarningType) {
+ case UsbPortStatus.COMPLIANCE_WARNING_OTHER:
+ case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
+ return true;
+ default:
+ break;
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index a93524f..51164e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -126,13 +126,25 @@
}
public List<BluetoothDevice> getConnectedDevices() {
+ return getDevicesByStates(new int[] {
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public List<BluetoothDevice> getConnectableDevices() {
+ return getDevicesByStates(new int[] {
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ private List<BluetoothDevice> getDevicesByStates(int[] states) {
if (mService == null) {
- return new ArrayList<BluetoothDevice>(0);
+ return new ArrayList<>(0);
}
- return mService.getDevicesMatchingConnectionStates(
- new int[] {BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_CONNECTING,
- BluetoothProfile.STATE_DISCONNECTING});
+ return mService.getDevicesMatchingConnectionStates(states);
}
/*
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0e7b79b..119aef6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -136,7 +136,7 @@
/**
* create profile instance according to bluetooth supported profile list
*/
- void updateLocalProfiles() {
+ synchronized void updateLocalProfiles() {
List<Integer> supportedList = BluetoothAdapter.getDefaultAdapter().getSupportedProfiles();
if (CollectionUtils.isEmpty(supportedList)) {
if (DEBUG) Log.d(TAG, "supportedList is null");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 29846ac..a88a9c7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
@@ -36,6 +37,7 @@
import android.os.BatteryManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
@@ -44,6 +46,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@@ -85,6 +88,8 @@
@Mock
private UsbPortStatus mUsbPortStatus;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -425,8 +430,38 @@
}
@Test
- public void containsIncompatibleChargers_complianeWarningOther_returnTrue() {
+ public void containsIncompatibleChargers_complianeWarningOther_returnTrue_flagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningPower_returnFalse_flagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningOther_returnFalse_flagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningPower_returnTrue_flagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
+
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@@ -446,7 +481,6 @@
public void containsIncompatibleChargers_emptyComplianceWarnings_returnFalse() {
setupIncompatibleCharging();
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
-
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@@ -476,7 +510,7 @@
}
private void setupIncompatibleCharging() {
- setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY);
}
private void setupIncompatibleCharging(int complianceWarningType) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
index 942e915..74a282f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/MainSwitchBarTest.java
@@ -21,30 +21,25 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
-import android.text.TextUtils;
import android.view.View;
-import android.widget.Switch;
+import android.widget.CompoundButton;
import android.widget.TextView;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.settingslib.widget.mainswitch.R;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class MainSwitchBarTest {
- private Context mContext;
- private MainSwitchBar mBar;
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final MainSwitchBar mBar = new MainSwitchBar(mContext);
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
- mBar = new MainSwitchBar(mContext);
- }
+ private final CompoundButton mSwitch = mBar.findViewById(android.R.id.switch_widget);
@Test
public void setChecked_true_shouldChecked() {
@@ -60,7 +55,7 @@
mBar.setTitle(title);
final TextView textView = ((TextView) mBar.findViewById(R.id.switch_text));
- assertThat(textView.getText()).isEqualTo(title);
+ assertThat(textView.getText().toString()).isEqualTo(title);
}
@Test
@@ -69,23 +64,18 @@
mBar.setTitle(title);
- final Switch switchObj = mBar.getSwitch();
- assertThat(TextUtils.isEmpty(switchObj.getContentDescription())).isTrue();
+ assertThat(mSwitch.getContentDescription()).isNull();
}
@Test
public void getSwitch_shouldNotNull() {
- final Switch switchObj = mBar.getSwitch();
-
- assertThat(switchObj).isNotNull();
+ assertThat(mSwitch).isNotNull();
}
@Test
public void getSwitch_shouldNotFocusableAndClickable() {
- final Switch switchObj = mBar.getSwitch();
-
- assertThat(switchObj.isFocusable()).isFalse();
- assertThat(switchObj.isClickable()).isFalse();
+ assertThat(mSwitch.isFocusable()).isFalse();
+ assertThat(mSwitch.isClickable()).isFalse();
}
@Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 6bc2716..6ff36d4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -29,6 +29,7 @@
import android.content.AttributionSource;
import android.content.IContentProvider;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -219,14 +220,33 @@
return lines;
}
+ private static void log(String msg) {
+ if (Build.IS_DEBUGGABLE) {
+ Slog.wtf(TAG, msg);
+ } else {
+ Slog.e(TAG, msg);
+ }
+ }
+
public static List<String> listAllAconfigFlags(IContentProvider provider) {
HashMap<String, String> allFlags = getAllFlags(provider);
HashSet<String> aconfigFlagNames = getAconfigFlagNamesInDeviceConfig();
final ArrayList<String> lines = new ArrayList<>();
- for (String key : aconfigFlagNames) {
- String val = allFlags.get(key);
+ for (String aconfigFlag : aconfigFlagNames) {
+ String val = allFlags.get(aconfigFlag);
if (val != null) {
- lines.add(key + "=" + val);
+ // aconfigFlag is in the form of [namespace]/[package].[flag_name]
+ int idx = aconfigFlag.indexOf("/");
+ if (idx == -1 || idx == aconfigFlag.length() - 1 || idx == 0) {
+ log("invalid flag entry in device config: " + aconfigFlag);
+ continue;
+ }
+
+ // we intend to print out [package].[flag_name] [namespace]=val
+ String aconfigFlagNameByPackage = aconfigFlag.substring(idx + 1);
+ String namespace = aconfigFlag.substring(0, idx);
+ lines.add("flag:" + aconfigFlagNameByPackage + " namespace:" + namespace +
+ " value:" + val);
}
}
Collections.sort(lines);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c7e5bf9..ed03d94 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -592,6 +592,9 @@
<!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo -->
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+ <!-- Permission required for CTS test - CtsThreadNetworkTestCases -->
+ <uses-permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"/>
+
<!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
<uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
@@ -864,6 +867,7 @@
<!-- Permissions required for CTS test - CtsVoiceInteractionTestCases -->
<uses-permission android:name="android.permission.RESET_HOTWORD_TRAINING_DATA_EGRESS_COUNT" />
<uses-permission android:name="android.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA" />
+ <uses-permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE" />
<application
android:label="@string/app_label"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 88abf69..0e9f8b1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -187,6 +187,8 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
+ "androidx.room_room-runtime",
+ "androidx.room_room-ktx",
"com.google.android.material_material",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
@@ -207,10 +209,16 @@
],
manifest: "AndroidManifest.xml",
- javacflags: ["-Adagger.fastInit=enabled"],
+ javacflags: [
+ "-Adagger.fastInit=enabled",
+ "-Aroom.schemaLocation=frameworks/base/packages/SystemUI/schemas",
+ ],
kotlincflags: ["-Xjvm-default=all"],
- plugins: ["dagger2-compiler"],
+ plugins: [
+ "androidx.room_room-compiler-plugin",
+ "dagger2-compiler",
+ ],
lint: {
extra_check_modules: ["SystemUILintChecker"],
@@ -466,6 +474,8 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
+ "androidx.room_room-runtime",
+ "androidx.room_room-ktx",
"kotlinx-coroutines-android",
"kotlinx-coroutines-core",
"kotlinx_coroutines_test",
@@ -530,7 +540,10 @@
"--extra-packages",
"com.android.systemui",
],
- plugins: ["dagger2-compiler"],
+ plugins: [
+ "androidx.room_room-compiler-plugin",
+ "dagger2-compiler",
+ ],
lint: {
test: true,
extra_check_modules: ["SystemUILintChecker"],
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 28539dd..0480b9d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -88,20 +88,6 @@
"include-filter": "android.permissionui.cts.CameraMicIndicatorsPermissionTest"
}
]
- },
- {
- "name": "SystemUIGoogleScreenshotTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
}
],
@@ -171,12 +157,6 @@
},
{
"include-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.Postsubmit"
}
]
},
@@ -188,28 +168,18 @@
},
{
"include-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.Postsubmit"
}
]
},
- { "name": "SystemUIGoogleBiometricsScreenshotTests",
+ {
+ // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+)
+ "name": "SystemUIGoogleBiometricsScreenshotTests",
"options": [
{
"exclude-annotation": "org.junit.Ignore"
},
{
"include-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.FlakyTest"
- },
- {
- "include-annotation": "android.platform.test.annotations.Postsubmit"
}
]
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index 96e1e3f..085fc29 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -63,7 +63,7 @@
private static final String TAG = "A11yMenuService";
private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
- private static final long TAKE_SCREENSHOT_DELAY_MS = 100L;
+ private static final long HIDE_UI_DELAY_MS = 100L;
private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
(int) Math.ceil(BrightnessUtils.GAMMA_SPACE_MAX * 0.11f);
@@ -296,7 +296,14 @@
} else if (viewTag == ShortcutId.ID_RECENT_VALUE.ordinal()) {
performGlobalActionInternal(GLOBAL_ACTION_RECENTS);
} else if (viewTag == ShortcutId.ID_LOCKSCREEN_VALUE.ordinal()) {
- performGlobalActionInternal(GLOBAL_ACTION_LOCK_SCREEN);
+ if (Flags.a11yMenuHideBeforeTakingAction()) {
+ // Delay before locking the screen to give time for the UI to close.
+ mHandler.postDelayed(
+ () -> performGlobalActionInternal(GLOBAL_ACTION_LOCK_SCREEN),
+ HIDE_UI_DELAY_MS);
+ } else {
+ performGlobalActionInternal(GLOBAL_ACTION_LOCK_SCREEN);
+ }
} else if (viewTag == ShortcutId.ID_QUICKSETTING_VALUE.ordinal()) {
performGlobalActionInternal(GLOBAL_ACTION_QUICK_SETTINGS);
} else if (viewTag == ShortcutId.ID_NOTIFICATION_VALUE.ordinal()) {
@@ -306,7 +313,7 @@
// Delay before taking a screenshot to give time for the UI to close.
mHandler.postDelayed(
() -> performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT),
- TAKE_SCREENSHOT_DELAY_MS);
+ HIDE_UI_DELAY_MS);
} else {
performGlobalActionInternal(GLOBAL_ACTION_TAKE_SCREENSHOT);
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 8019b38..9700bc6 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -46,6 +46,14 @@
}
flag {
+ name: "notifications_live_data_store_refactor"
+ namespace: "systemui"
+ description: "Replaces NotifLiveDataStore with ActiveNotificationListRepository, and updates consumers. "
+ "Should not bring any behavior changes."
+ bug: "308623704"
+}
+
+flag {
name: "scene_container"
namespace: "systemui"
description: "Enables the scene container framework go/flexiglass."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 56970d7..defaa20 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -25,7 +25,9 @@
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
+import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -68,7 +70,6 @@
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.LayoutDirection
@@ -77,7 +78,9 @@
import com.android.compose.PlatformButton
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.thenIf
import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -154,7 +157,7 @@
Bouncer(
viewModel = viewModel,
dialogFactory = dialogFactory,
- isUserInputAreaVisible = true,
+ userInputAreaVisibility = UserInputAreaVisibility.FULL,
modifier = childModifier,
)
Layout.SIDE_BY_SIDE ->
@@ -189,12 +192,13 @@
private fun Bouncer(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
- isUserInputAreaVisible: Boolean,
+ userInputAreaVisibility: UserInputAreaVisibility,
modifier: Modifier = Modifier,
) {
val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
var dialog: Dialog? by remember { mutableStateOf(null) }
+ val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -214,29 +218,14 @@
}
Box(Modifier.weight(1f)) {
- if (isUserInputAreaVisible) {
- UserInputArea(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- }
+ UserInputArea(
+ viewModel = viewModel,
+ visibility = userInputAreaVisibility,
+ modifier = Modifier.align(Alignment.Center),
+ )
}
- if (viewModel.isEmergencyButtonVisible) {
- Button(
- onClick = viewModel::onEmergencyServicesButtonClicked,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.tertiaryContainer,
- contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
- ),
- ) {
- Text(
- text = stringResource(com.android.internal.R.string.lockscreen_emergency_call),
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- }
+ actionButton?.let { BouncerActionButton(viewModel = it) }
if (dialogMessage != null) {
if (dialog == null) {
@@ -269,6 +258,7 @@
@Composable
private fun UserInputArea(
viewModel: BouncerViewModel,
+ visibility: UserInputAreaVisibility,
modifier: Modifier = Modifier,
) {
val authMethodViewModel: AuthMethodBouncerViewModel? by
@@ -276,25 +266,81 @@
when (val nonNullViewModel = authMethodViewModel) {
is PinBouncerViewModel ->
- PinBouncer(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
+ when (visibility) {
+ UserInputAreaVisibility.FULL ->
+ PinBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ UserInputAreaVisibility.INPUT_ONLY ->
+ PinPad(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ UserInputAreaVisibility.OUTPUT_ONLY ->
+ PinInputDisplay(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ UserInputAreaVisibility.NONE -> {}
+ }
is PasswordBouncerViewModel ->
- PasswordBouncer(
- viewModel = nonNullViewModel,
- modifier = modifier,
- )
+ when (visibility) {
+ UserInputAreaVisibility.FULL,
+ UserInputAreaVisibility.INPUT_ONLY ->
+ PasswordBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ else -> {}
+ }
is PatternBouncerViewModel ->
- PatternBouncer(
- viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
- )
+ when (visibility) {
+ UserInputAreaVisibility.FULL,
+ UserInputAreaVisibility.INPUT_ONLY ->
+ PatternBouncer(
+ viewModel = nonNullViewModel,
+ modifier =
+ Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+ .then(modifier)
+ )
+ else -> {}
+ }
else -> Unit
}
}
+/**
+ * Renders the action button on the bouncer, which triggers either Return to Call or Emergency Call.
+ */
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun BouncerActionButton(
+ viewModel: BouncerActionButtonModel,
+ modifier: Modifier = Modifier,
+) {
+ Button(
+ onClick = viewModel.onClick,
+ modifier =
+ modifier.thenIf(viewModel.onLongClick != null) {
+ Modifier.combinedClickable(
+ onClick = viewModel.onClick,
+ onLongClick = viewModel.onLongClick,
+ )
+ },
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ ),
+ ) {
+ Text(
+ text = viewModel.label,
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ }
+}
+
/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@Composable
private fun UserSwitcher(
@@ -435,13 +481,14 @@
Bouncer(
viewModel = viewModel,
dialogFactory = dialogFactory,
- isUserInputAreaVisible = false,
+ userInputAreaVisibility = UserInputAreaVisibility.OUTPUT_ONLY,
modifier = startContentModifier,
)
},
endContent = { endContentModifier ->
UserInputArea(
viewModel = viewModel,
+ visibility = UserInputAreaVisibility.INPUT_ONLY,
modifier = endContentModifier,
)
},
@@ -545,7 +592,7 @@
Bouncer(
viewModel = viewModel,
dialogFactory = dialogFactory,
- isUserInputAreaVisible = true,
+ userInputAreaVisibility = UserInputAreaVisibility.FULL,
modifier = endContentModifier,
)
},
@@ -574,7 +621,7 @@
Bouncer(
viewModel = viewModel,
dialogFactory = dialogFactory,
- isUserInputAreaVisible = true,
+ userInputAreaVisibility = UserInputAreaVisibility.FULL,
modifier = Modifier.fillMaxWidth().weight(1f),
)
}
@@ -630,6 +677,27 @@
SPLIT,
}
+/** Enumerates all supported user-input area visibilities. */
+private enum class UserInputAreaVisibility {
+ /**
+ * The entire user input area is shown, including where the user enters input and where it's
+ * reflected to the user.
+ */
+ FULL,
+ /**
+ * Only the area where the user enters the input is shown; the area where the input is reflected
+ * back to the user is not shown.
+ */
+ INPUT_ONLY,
+ /**
+ * Only the area where the input is reflected back to the user is shown; the area where the
+ * input is entered by the user is not shown.
+ */
+ OUTPUT_ONLY,
+ /** The entire user input area is hidden. */
+ NONE,
+}
+
/**
* Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
* the two reaches a stopping point but `0` in the middle of the transition.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 6491b70..84e0167 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -55,12 +55,12 @@
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.compose.modifiers.thenIf
-import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.res.R
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
import kotlinx.coroutines.async
@@ -93,7 +93,10 @@
}
@Composable
-private fun PinPad(viewModel: PinBouncerViewModel) {
+fun PinPad(
+ viewModel: PinBouncerViewModel,
+ modifier: Modifier = Modifier,
+) {
val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
@@ -112,6 +115,7 @@
columns = 3,
verticalSpacing = 12.dp,
horizontalSpacing = 20.dp,
+ modifier = modifier,
) {
repeat(9) { index ->
DigitButton(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 055ece3..814ea31 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -51,10 +51,10 @@
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.keyguard.PinShapeAdapter
-import com.android.systemui.res.R
import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinInputViewModel
+import com.android.systemui.res.R
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
@@ -65,7 +65,10 @@
import kotlinx.coroutines.launch
@Composable
-fun PinInputDisplay(viewModel: PinBouncerViewModel) {
+fun PinInputDisplay(
+ viewModel: PinBouncerViewModel,
+ modifier: Modifier = Modifier,
+) {
val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState()
val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)
@@ -81,8 +84,8 @@
// unifying into a single, more complex implementation.
when (val length = hintedPinLength) {
- null -> RegularPinInputDisplay(viewModel, shapeAnimations)
- else -> HintingPinInputDisplay(viewModel, shapeAnimations, length)
+ null -> RegularPinInputDisplay(viewModel, shapeAnimations, modifier)
+ else -> HintingPinInputDisplay(viewModel, shapeAnimations, length, modifier)
}
}
@@ -97,6 +100,7 @@
viewModel: PinBouncerViewModel,
shapeAnimations: ShapeAnimations,
hintedPinLength: Int,
+ modifier: Modifier = Modifier,
) {
val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState()
// [ClearAll] marker pointing at the beginning of the current pin input.
@@ -151,7 +155,7 @@
LaunchedEffect(Unit) { playAnimation = true }
val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
- Row(modifier = Modifier.heightIn(min = shapeAnimations.shapeSize)) {
+ Row(modifier = modifier.heightIn(min = shapeAnimations.shapeSize)) {
pinEntryDrawable.forEachIndexed { index, drawable ->
// Key the loop by [index] and [drawable], so that updating a shape drawable at the same
// index will play the new animation (by remembering a new [atEnd]).
@@ -183,6 +187,7 @@
private fun RegularPinInputDisplay(
viewModel: PinBouncerViewModel,
shapeAnimations: ShapeAnimations,
+ modifier: Modifier = Modifier,
) {
// Holds all currently [VisiblePinEntry] composables. This cannot be simply derived from
// `viewModel.pinInput` at composition, since deleting a pin entry needs to play a remove
@@ -226,7 +231,7 @@
}
}
- pinInputRow.Content()
+ pinInputRow.Content(modifier)
}
private class PinInputRow(
@@ -235,10 +240,11 @@
private val entries = mutableStateListOf<PinInputEntry>()
@Composable
- fun Content() {
+ fun Content(modifier: Modifier) {
Row(
modifier =
- Modifier.heightIn(min = shapeAnimations.shapeSize)
+ modifier
+ .heightIn(min = shapeAnimations.shapeSize)
// Pins overflowing horizontally should still be shown as scrolling.
.wrapContentSize(unbounded = true),
) {
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 87a8c35..1429782 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
@@ -1,38 +1,64 @@
+/*
+ * 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.ui.compose
-import android.appwidget.AppWidgetHostView
import android.os.Bundle
import android.util.SizeF
+import android.widget.FrameLayout
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Card
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
+import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.ui.model.CommunalContentUiModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.res.R
@Composable
fun CommunalHub(
modifier: Modifier = Modifier,
viewModel: CommunalViewModel,
) {
- val showTutorial by viewModel.showTutorialContent.collectAsState(initial = false)
- val widgetContent by viewModel.widgetContent.collectAsState(initial = emptyList())
+ val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
Box(
modifier = modifier.fillMaxSize().background(Color.White),
) {
@@ -42,56 +68,121 @@
horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
) {
- if (showTutorial) {
- items(
- count = tutorialContentSizes.size,
- // TODO(b/308148193): a more scalable solution for unique ids.
- key = { index -> "tutorial_$index" },
- span = { index -> GridItemSpan(tutorialContentSizes[index].span) },
- ) { index ->
- TutorialCard(
- modifier =
- Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()),
- )
- }
- } else {
- items(
- count = widgetContent.size,
- key = { index -> widgetContent[index].id },
- span = { index -> GridItemSpan(widgetContent[index].size.span) },
- ) { index ->
- val widget = widgetContent[index]
- ContentCard(
- modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()),
- model = widget,
- )
- }
+ items(
+ count = communalContent.size,
+ key = { index -> communalContent[index].key },
+ span = { index -> GridItemSpan(communalContent[index].size.span) },
+ ) { index ->
+ CommunalContent(
+ modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth),
+ model = communalContent[index],
+ viewModel = viewModel,
+ deleteOnClick = viewModel::onDeleteWidget,
+ size =
+ SizeF(
+ Dimensions.CardWidth.value,
+ communalContent[index].size.dp().value,
+ ),
+ )
}
}
+ IconButton(onClick = viewModel::onOpenWidgetPicker) {
+ Icon(
+ Icons.Default.Add,
+ LocalContext.current.getString(R.string.button_to_open_widget_picker)
+ )
+ }
}
}
-// A placeholder for tutorial content.
@Composable
-private fun TutorialCard(modifier: Modifier = Modifier) {
- Card(modifier = modifier, content = {})
+private fun CommunalContent(
+ model: CommunalContentModel,
+ viewModel: CommunalViewModel,
+ size: SizeF,
+ deleteOnClick: (id: Int) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ when (model) {
+ is CommunalContentModel.Widget -> WidgetContent(model, size, deleteOnClick, modifier)
+ is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
+ is CommunalContentModel.Tutorial -> TutorialContent(modifier)
+ is CommunalContentModel.Umo -> Umo(viewModel, modifier)
+ }
}
@Composable
-private fun ContentCard(
- model: CommunalContentUiModel,
+private fun WidgetContent(
+ model: CommunalContentModel.Widget,
+ size: SizeF,
+ deleteOnClick: (id: Int) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ // TODO(b/309009246): update background color
+ Box(
+ modifier = modifier.fillMaxSize().background(Color.White),
+ ) {
+ IconButton(onClick = { deleteOnClick(model.appWidgetId) }) {
+ Icon(
+ Icons.Default.Close,
+ LocalContext.current.getString(R.string.button_to_remove_widget)
+ )
+ }
+ AndroidView(
+ modifier = modifier,
+ factory = { context ->
+ model.appWidgetHost
+ .createView(context, model.appWidgetId, model.providerInfo)
+ .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
+ },
+ // For reusing composition in lazy lists.
+ onReset = {}
+ )
+ }
+}
+
+@Composable
+private fun SmartspaceContent(
+ model: CommunalContentModel.Smartspace,
modifier: Modifier = Modifier,
) {
AndroidView(
modifier = modifier,
- factory = {
- model.view.apply {
- if (this is AppWidgetHostView) {
- val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value)
- updateAppWidgetSize(Bundle.EMPTY, listOf(size))
- }
- }
+ factory = { context ->
+ FrameLayout(context).apply { addView(model.remoteViews.apply(context, this)) }
},
+ // For reusing composition in lazy lists.
+ onReset = {}
+ )
+}
+
+@Composable
+private fun TutorialContent(modifier: Modifier = Modifier) {
+ Card(modifier = modifier, content = {})
+}
+
+@Composable
+private fun Umo(viewModel: CommunalViewModel, modifier: Modifier = Modifier) {
+ AndroidView(
+ modifier =
+ modifier
+ .width(Dimensions.CardWidth)
+ .height(Dimensions.CardHeightThird)
+ .padding(Dimensions.Spacing),
+ factory = {
+ viewModel.mediaHost.expansion = MediaHostState.EXPANDED
+ viewModel.mediaHost.showsOnlyActiveMedia = false
+ viewModel.mediaHost.falsingProtectionNeeded = false
+ viewModel.mediaHost.init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
+ viewModel.mediaHost.hostView.layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ viewModel.mediaHost.hostView
+ },
+ // For reusing composition in lazy lists.
+ onReset = {},
)
}
@@ -103,19 +194,6 @@
}
}
-// Sizes for the tutorial placeholders.
-private val tutorialContentSizes =
- listOf(
- CommunalContentSize.FULL,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.THIRD,
- CommunalContentSize.HALF,
- CommunalContentSize.HALF,
- CommunalContentSize.HALF,
- CommunalContentSize.HALF,
- )
-
private object Dimensions {
val CardWidth = 464.dp
val CardHeightFull = 630.dp
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt
new file mode 100644
index 0000000..de46f72
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.compose.animation.scene
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+
+/** `SceneScope` for tests, which allows a single scene to be drawn in a [SceneTransitionLayout]. */
+@Composable
+fun TestSceneScope(
+ modifier: Modifier = Modifier,
+ content: @Composable SceneScope.() -> Unit,
+) {
+ val currentScene = remember { SceneKey("current") }
+ SceneTransitionLayout(
+ currentScene,
+ onChangeScene = { /* do nothing */},
+ transitions = remember { transitions {} },
+ modifier,
+ ) {
+ scene(currentScene, content = content)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt
new file mode 100644
index 0000000..c2117ae
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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.bouncer.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.scene.SceneTestUtils
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class EmergencyServicesRepositoryImplTest : SysuiTestCase() {
+
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+
+ private lateinit var underTest: EmergencyServicesRepository
+
+ @Before
+ fun setUp() {
+ overrideResource(
+ R.bool.config_enable_emergency_call_while_sim_locked,
+ ENABLE_EMERGENCY_CALL_WHILE_SIM_LOCKED
+ )
+
+ underTest =
+ EmergencyServicesRepository(
+ resources = context.resources,
+ applicationScope = testScope.backgroundScope,
+ configurationRepository = utils.configurationRepository,
+ )
+ }
+
+ @Test
+ fun enableEmergencyCallWhileSimLocked() =
+ testScope.runTest {
+ val enableEmergencyCallWhileSimLocked by
+ collectLastValue(underTest.enableEmergencyCallWhileSimLocked)
+
+ setEmergencyCallWhileSimLocked(isEnabled = false)
+ assertThat(enableEmergencyCallWhileSimLocked).isFalse()
+
+ setEmergencyCallWhileSimLocked(isEnabled = true)
+ assertThat(enableEmergencyCallWhileSimLocked).isTrue()
+ }
+
+ private fun TestScope.setEmergencyCallWhileSimLocked(isEnabled: Boolean) {
+ overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, isEnabled)
+ utils.configurationRepository.onConfigurationChange()
+ runCurrent()
+ }
+
+ companion object {
+ private const val ENABLE_EMERGENCY_CALL_WHILE_SIM_LOCKED = true
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
new file mode 100644
index 0000000..fde3ad7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright 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.bouncer.domain.interactor
+
+import android.app.ActivityTaskManager
+import android.telecom.TelecomManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.internal.logging.nano.MetricsProto
+import com.android.internal.logging.testing.FakeMetricsLogger
+import com.android.internal.util.EmergencyAffordanceManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.model.AuthenticationMethodModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+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.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BouncerActionButtonInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var activityTaskManager: ActivityTaskManager
+ @Mock private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
+ @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
+ @Mock private lateinit var tableLogger: TableLogBuffer
+ @Mock private lateinit var telecomManager: TelecomManager
+
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+ private val metricsLogger = FakeMetricsLogger()
+ private var currentUserId: Int = 0
+ private var needsEmergencyAffordance = true
+
+ private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
+
+ private lateinit var underTest: BouncerActionButtonInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ overrideResource(R.string.lockscreen_emergency_call, MESSAGE_EMERGENCY_CALL)
+ overrideResource(R.string.lockscreen_return_to_call, MESSAGE_RETURN_TO_CALL)
+ overrideResource(
+ R.bool.config_enable_emergency_call_while_sim_locked,
+ ENABLE_EMERGENCY_CALL_WHILE_SIM_LOCKED
+ )
+ whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUserId)
+ whenever(emergencyAffordanceManager.needsEmergencyAffordance())
+ .thenReturn(needsEmergencyAffordance)
+ whenever(telecomManager.isInCall).thenReturn(false)
+
+ utils.featureFlags.set(REFACTOR_GETCURRENTUSER, true)
+
+ mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
+
+ utils.telephonyRepository.setHasTelephonyRadio(true)
+
+ underTest =
+ utils.bouncerActionButtonInteractor(
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ activityTaskManager = activityTaskManager,
+ telecomManager = telecomManager,
+ emergencyAffordanceManager = emergencyAffordanceManager,
+ metricsLogger = metricsLogger,
+ )
+ }
+
+ @Test
+ fun noTelephonyRadio_noButton() =
+ testScope.runTest {
+ utils.telephonyRepository.setHasTelephonyRadio(false)
+ underTest =
+ utils.bouncerActionButtonInteractor(
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ activityTaskManager = activityTaskManager,
+ telecomManager = telecomManager,
+ )
+
+ val actionButton by collectLastValue(underTest.actionButton)
+ assertThat(actionButton).isNull()
+ }
+
+ @Test
+ fun noTelecomManager_noButton() =
+ testScope.runTest {
+ underTest =
+ utils.bouncerActionButtonInteractor(
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ activityTaskManager = activityTaskManager,
+ telecomManager = null,
+ )
+ val actionButton by collectLastValue(underTest.actionButton)
+ assertThat(actionButton).isNull()
+ }
+
+ @Test
+ fun duringCall_returnToCallButton() =
+ testScope.runTest {
+ val actionButton by collectLastValue(underTest.actionButton)
+ utils.telephonyRepository.setIsInCall(true)
+
+ assertThat(actionButton).isNotNull()
+ assertThat(actionButton?.label).isEqualTo(MESSAGE_RETURN_TO_CALL)
+ assertThat(actionButton?.onClick).isNotNull()
+ assertThat(actionButton?.onLongClick).isNull()
+
+ actionButton?.onClick?.invoke()
+
+ assertThat(metricsLogger.logs.size).isEqualTo(1)
+ assertThat(metricsLogger.logs.element().category)
+ .isEqualTo(MetricsProto.MetricsEvent.ACTION_EMERGENCY_CALL)
+ verify(activityTaskManager).stopSystemLockTaskMode()
+ verify(telecomManager).showInCallScreen(eq(false))
+ }
+
+ @Test
+ fun noCall_secureAuthMethod_emergencyCallButton() =
+ testScope.runTest {
+ val actionButton by collectLastValue(underTest.actionButton)
+ mobileConnectionsRepository.isAnySimSecure.value = false
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+ utils.telephonyRepository.setIsInCall(false)
+
+ assertThat(actionButton).isNotNull()
+ assertThat(actionButton?.label).isEqualTo(MESSAGE_EMERGENCY_CALL)
+ assertThat(actionButton?.onClick).isNotNull()
+ assertThat(actionButton?.onLongClick).isNotNull()
+
+ actionButton?.onClick?.invoke()
+
+ assertThat(metricsLogger.logs.size).isEqualTo(1)
+ assertThat(metricsLogger.logs.element().category)
+ .isEqualTo(MetricsProto.MetricsEvent.ACTION_EMERGENCY_CALL)
+ verify(activityTaskManager).stopSystemLockTaskMode()
+
+ // TODO(b/25189994): Test the activity has been started once we switch to the
+ // ActivityStarter interface here.
+ verify(emergencyAffordanceManager, never()).performEmergencyCall()
+
+ actionButton?.onLongClick?.invoke()
+ verify(emergencyAffordanceManager).performEmergencyCall()
+ }
+
+ @Test
+ fun noCall_insecureAuthMethodButSecureSim_emergencyCallButton() =
+ testScope.runTest {
+ val actionButton by collectLastValue(underTest.actionButton)
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.telephonyRepository.setIsInCall(false)
+
+ assertThat(actionButton).isNotNull()
+ assertThat(actionButton?.label).isEqualTo(MESSAGE_EMERGENCY_CALL)
+ assertThat(actionButton?.onClick).isNotNull()
+ assertThat(actionButton?.onLongClick).isNotNull()
+ }
+
+ @Test
+ fun noCall_insecure_noButton() =
+ testScope.runTest {
+ val actionButton by collectLastValue(underTest.actionButton)
+ mobileConnectionsRepository.isAnySimSecure.value = false
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.telephonyRepository.setIsInCall(false)
+
+ assertThat(actionButton).isNull()
+ }
+
+ @Test
+ fun noCall_simSecureButEmergencyNotSupported_noButton() =
+ testScope.runTest {
+ val actionButton by collectLastValue(underTest.actionButton)
+ mobileConnectionsRepository.isAnySimSecure.value = true
+ overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, false)
+ utils.configurationRepository.onConfigurationChange()
+ utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+ utils.telephonyRepository.setIsInCall(false)
+ runCurrent()
+
+ assertThat(actionButton).isNull()
+ }
+
+ companion object {
+ private const val MESSAGE_EMERGENCY_CALL = "Emergency"
+ private const val MESSAGE_RETURN_TO_CALL = "Return to call"
+ private const val ENABLE_EMERGENCY_CALL_WHILE_SIM_LOCKED = true
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
new file mode 100644
index 0000000..7196de6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalRepositoryImplTest : SysuiTestCase() {
+ private lateinit var underTest: CommunalRepositoryImpl
+
+ private lateinit var testScope: TestScope
+
+ private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
+ private lateinit var sceneContainerFlags: FakeSceneContainerFlags
+ private lateinit var sceneContainerRepository: SceneContainerRepository
+
+ @Before
+ fun setUp() {
+ testScope = TestScope()
+
+ val sceneTestUtils = SceneTestUtils(this)
+ sceneContainerFlags = FakeSceneContainerFlags(enabled = false)
+ sceneContainerRepository = sceneTestUtils.fakeSceneContainerRepository()
+ featureFlagsClassic = FakeFeatureFlagsClassic()
+
+ featureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+
+ underTest =
+ CommunalRepositoryImpl(
+ featureFlagsClassic,
+ sceneContainerFlags,
+ sceneContainerRepository,
+ )
+ }
+
+ @Test
+ fun isCommunalShowing_sceneContainerDisabled_onCommunalScene_true() =
+ testScope.runTest {
+ underTest.setDesiredScene(CommunalSceneKey.Communal)
+
+ val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+ assertThat(isCommunalHubShowing).isTrue()
+ }
+
+ @Test
+ fun isCommunalShowing_sceneContainerDisabled_onBlankScene_false() =
+ testScope.runTest {
+ underTest.setDesiredScene(CommunalSceneKey.Blank)
+
+ val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+ assertThat(isCommunalHubShowing).isFalse()
+ }
+
+ @Test
+ fun isCommunalShowing_sceneContainerEnabled_onCommunalScene_true() =
+ testScope.runTest {
+ sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
+ underTest =
+ CommunalRepositoryImpl(
+ featureFlagsClassic,
+ sceneContainerFlags,
+ sceneContainerRepository,
+ )
+
+ sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Communal))
+
+ val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+ assertThat(isCommunalHubShowing).isTrue()
+ }
+
+ @Test
+ fun isCommunalShowing_sceneContainerEnabled_onLockscreenScene_false() =
+ testScope.runTest {
+ sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
+ underTest =
+ CommunalRepositoryImpl(
+ featureFlagsClassic,
+ sceneContainerFlags,
+ sceneContainerRepository,
+ )
+
+ sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Lockscreen))
+
+ val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+ assertThat(isCommunalHubShowing).isFalse()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
new file mode 100644
index 0000000..9a3129f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.domain.interactor
+
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalTutorialInteractorTest : SysuiTestCase() {
+
+ @Mock private lateinit var userTracker: UserTracker
+
+ private lateinit var testScope: TestScope
+ private lateinit var underTest: CommunalTutorialInteractor
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
+ private lateinit var communalRepository: FakeCommunalRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+
+ val withDeps = CommunalTutorialInteractorFactory.create(testScope)
+ keyguardRepository = withDeps.keyguardRepository
+ communalTutorialRepository = withDeps.communalTutorialRepository
+ communalRepository = withDeps.communalRepository
+
+ underTest = withDeps.communalTutorialInteractor
+
+ whenever(userTracker.userHandle).thenReturn(mock())
+ }
+
+ @Test
+ fun tutorialUnavailable_whenKeyguardNotVisible() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+ keyguardRepository.setKeyguardShowing(false)
+ assertThat(isTutorialAvailable).isFalse()
+ }
+
+ @Test
+ fun tutorialUnavailable_whenTutorialIsCompleted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalRepository.setIsCommunalHubShowing(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ assertThat(isTutorialAvailable).isFalse()
+ }
+
+ @Test
+ fun tutorialAvailable_whenTutorialNotStarted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalRepository.setIsCommunalHubShowing(false)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+ assertThat(isTutorialAvailable).isTrue()
+ }
+
+ @Test
+ fun tutorialAvailable_whenTutorialIsStarted() =
+ testScope.runTest {
+ val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ communalRepository.setIsCommunalHubShowing(true)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+ assertThat(isTutorialAvailable).isTrue()
+ }
+
+ @Test
+ fun tutorialState_notStartedAndCommunalSceneShowing_tutorialStarted() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+ communalRepository.setIsCommunalHubShowing(true)
+
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+ communalRepository.setIsCommunalHubShowing(true)
+
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+ }
+
+ @Test
+ fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ communalRepository.setIsCommunalHubShowing(true)
+
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ @Test
+ fun tutorialState_notStartedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+ communalRepository.setIsCommunalHubShowing(false)
+
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
+ }
+
+ @Test
+ fun tutorialState_startedAndCommunalSceneNotShowing_tutorialCompleted() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ communalRepository.setIsCommunalHubShowing(true)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+ communalRepository.setIsCommunalHubShowing(false)
+
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+
+ @Test
+ fun tutorialState_completedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+ testScope.runTest {
+ val tutorialSettingState by
+ collectLastValue(communalTutorialRepository.tutorialSettingState)
+ communalRepository.setIsCommunalHubShowing(true)
+ communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ communalRepository.setIsCommunalHubShowing(false)
+
+ assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
new file mode 100644
index 0000000..262795f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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.telephony.data.repository
+
+import android.telecom.TelecomManager
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.telephony.TelephonyListenerManager
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TelephonyRepositoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var manager: TelephonyListenerManager
+ @Mock private lateinit var telecomManager: TelecomManager
+
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+
+ private lateinit var underTest: TelephonyRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(telecomManager.isInCall).thenReturn(false)
+
+ underTest =
+ TelephonyRepositoryImpl(
+ applicationScope = testScope.backgroundScope,
+ applicationContext = context,
+ backgroundDispatcher = utils.testDispatcher,
+ manager = manager,
+ telecomManager = telecomManager,
+ )
+ }
+
+ @Test
+ fun callState() =
+ testScope.runTest {
+ val callState by collectLastValue(underTest.callState)
+ runCurrent()
+
+ val listenerCaptor = kotlinArgumentCaptor<TelephonyCallback.CallStateListener>()
+ verify(manager).addCallStateListener(listenerCaptor.capture())
+ val listener = listenerCaptor.value
+
+ listener.onCallStateChanged(0)
+ assertThat(callState).isEqualTo(0)
+
+ listener.onCallStateChanged(1)
+ assertThat(callState).isEqualTo(1)
+
+ listener.onCallStateChanged(2)
+ assertThat(callState).isEqualTo(2)
+ }
+
+ @Test
+ fun isInCall() =
+ testScope.runTest {
+ val isInCall by collectLastValue(underTest.isInCall)
+ runCurrent()
+
+ val listenerCaptor = kotlinArgumentCaptor<TelephonyCallback.CallStateListener>()
+ verify(manager).addCallStateListener(listenerCaptor.capture())
+ val listener = listenerCaptor.value
+ whenever(telecomManager.isInCall).thenReturn(true)
+ listener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK)
+
+ assertThat(isInCall).isTrue()
+
+ whenever(telecomManager.isInCall).thenReturn(false)
+ listener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE)
+
+ assertThat(isInCall).isFalse()
+ }
+}
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index 1beb55b..0537f17 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -26,12 +26,19 @@
name: "SystemUIPluginLib",
srcs: [
- "src/**/*.java",
- "src/**/*.kt",
"bcsmartspace/src/**/*.java",
"bcsmartspace/src/**/*.kt",
+ "src/**/*.java",
+ "src/**/*.kt",
],
+ optimize: {
+ proguard_flags_files: [
+ "proguard_plugins.flags",
+ ],
+ export_proguard_flags_files: true,
+ },
+
// If you add a static lib here, you may need to also add the package to the ClassLoaderFilter
// in PluginInstance. That will ensure that loaded plugins have access to the related classes.
// You should also add it to proguard_common.flags so that proguard does not remove the portions
@@ -43,6 +50,7 @@
"SystemUIAnimationLib",
"SystemUICommon",
"SystemUILogLib",
+ "androidx.annotation_annotation",
],
}
diff --git a/packages/SystemUI/plugin/proguard_plugins.flags b/packages/SystemUI/plugin/proguard_plugins.flags
new file mode 100644
index 0000000..abac27f
--- /dev/null
+++ b/packages/SystemUI/plugin/proguard_plugins.flags
@@ -0,0 +1,9 @@
+# The plugins and core log subpackages act as shared libraries that might be referenced in
+# dynamically-loaded plugin APKs.
+-keep class com.android.systemui.plugins.** {
+ *;
+}
+
+-keep class com.android.systemui.log.core.** {
+ *;
+}
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index b534fcec..42b5923 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -4,4 +4,4 @@
*;
}
--keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.DaggerReferenceGlobalRootComponent** { !synthetic *; }
\ No newline at end of file
+-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.DaggerReferenceGlobalRootComponent** { !synthetic *; }
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 445bdc2..21b019e 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -1,3 +1,4 @@
+-include proguard_kotlin.flags
-keep class com.android.systemui.VendorServices
# Needed to ensure callback field references are kept in their respective
@@ -20,16 +21,6 @@
public <init>(android.content.Context, android.util.AttributeSet);
}
--keep class com.android.systemui.tuner.*
-
-# The plugins and core log subpackages act as shared libraries that might be referenced in
-# dynamically-loaded plugin APKs.
--keep class com.android.systemui.plugins.** {
- *;
-}
--keep class com.android.systemui.log.core.** {
- *;
-}
-keep class androidx.core.app.CoreComponentFactory
# Keep the wm shell lib
@@ -51,45 +42,6 @@
# part of optimization. This lets proguard inline trivial getter/setter methods.
-allowaccessmodification
-# Removes runtime checks added through Kotlin to JVM code genereration to
-# avoid linear growth as more Kotlin code is converted / added to the codebase.
-# These checks are generally applied to Java platform types (values returned
-# from Java code that don't have nullness annotations), but we remove them to
-# avoid code size increases.
-#
-# See also https://kotlinlang.org/docs/reference/java-interop.html
-#
-# TODO(b/199941987): Consider standardizing these rules in a central place as
-# Kotlin gains adoption with other platform targets.
--assumenosideeffects class kotlin.jvm.internal.Intrinsics {
- # Remove check for method parameters being null
- static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
-
- # When a Java platform type is returned and passed to Kotlin NonNull method,
- # remove the null check
- static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
- static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
-
- # Remove check that final value returned from method is null, if passing
- # back Java platform type.
- static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
- static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
-
- # Null check for accessing a field from a parent class written in Java.
- static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
- static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
-
- # Removes code generated from !! operator which converts Nullable type to
- # NonNull type. These would throw an NPE immediate after on access.
- static void checkNotNull(java.lang.Object, java.lang.String);
- static void checkNotNullParameter(java.lang.Object, java.lang.String);
-
- # Removes lateinit var check being used before being set. Check is applied
- # on every field access without this.
- static void throwUninitializedPropertyAccessException(java.lang.String);
-}
-
-
# Strip verbose logs.
-assumenosideeffects class android.util.Log {
static *** v(...);
diff --git a/packages/SystemUI/proguard_kotlin.flags b/packages/SystemUI/proguard_kotlin.flags
new file mode 100644
index 0000000..ceea3c8
--- /dev/null
+++ b/packages/SystemUI/proguard_kotlin.flags
@@ -0,0 +1,37 @@
+# Removes runtime checks added through Kotlin to JVM code genereration to
+# avoid linear growth as more Kotlin code is converted / added to the codebase.
+# These checks are generally applied to Java platform types (values returned
+# from Java code that don't have nullness annotations), but we remove them to
+# avoid code size increases.
+#
+# See also https://kotlinlang.org/docs/reference/java-interop.html
+#
+# TODO(b/199941987): Consider standardizing these rules in a central place as
+# Kotlin gains adoption with other platform targets.
+-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
+ # Remove check for method parameters being null
+ static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
+
+ # When a Java platform type is returned and passed to Kotlin NonNull method,
+ # remove the null check
+ static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
+ static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
+
+ # Remove check that final value returned from method is null, if passing
+ # back Java platform type.
+ static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+ static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
+
+ # Null check for accessing a field from a parent class written in Java.
+ static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
+ static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
+
+ # Removes code generated from !! operator which converts Nullable type to
+ # NonNull type. These would throw an NPE immediate after on access.
+ static void checkNotNull(java.lang.Object, java.lang.String);
+ static void checkNotNullParameter(java.lang.Object, java.lang.String);
+
+ # Removes lateinit var check being used before being set. Check is applied
+ # on every field access without this.
+ static void throwUninitializedPropertyAccessException(java.lang.String);
+}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1a37e2d..73ee50d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -736,6 +736,8 @@
<!-- Whether the communal service should be enabled -->
<bool name="config_communalServiceEnabled">false</bool>
+ <!-- Name of the database that stores info of widgets shown on glanceable hub -->
+ <string name="config_communalDatabase" translatable="false">communal_db</string>
<!-- Component names of allowed communal widgets -->
<string-array name="config_communalWidgetAllowlist" translatable="false" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 12bff4a..8780f58 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1048,6 +1048,11 @@
<!-- 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 removes a widget on click. [CHAR LIMIT=50] -->
+ <string name="button_to_remove_widget">Remove a widget</string>
+
<!-- Related to user switcher --><skip/>
<!-- Accessibility label for the button that opens the user switcher. -->
diff --git a/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/1.json b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/1.json
new file mode 100644
index 0000000..ffc4d91
--- /dev/null
+++ b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/1.json
@@ -0,0 +1,79 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 1,
+ "identityHash": "38f223811a414587ee1b6445ae19385d",
+ "entities": [
+ {
+ "tableName": "communal_widget_table",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "widgetId",
+ "columnName": "widget_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "componentName",
+ "columnName": "component_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "itemId",
+ "columnName": "item_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "uid"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "communal_item_rank_table",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "rank",
+ "columnName": "rank",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "uid"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '38f223811a414587ee1b6445ae19385d')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index a14f971..f005af3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -28,6 +28,7 @@
import android.graphics.RecordingCanvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.Looper;
import android.os.Trace;
import android.view.RenderNodeAnimator;
import android.view.View;
@@ -48,6 +49,7 @@
private static final float GLOW_MAX_ALPHA_DARK = 0.1f;
private static final int ANIMATION_DURATION_SCALE = 350;
private static final int ANIMATION_DURATION_FADE = 450;
+ private static final int ANIMATION_DURATION_FADE_FAST = 80;
private static final Interpolator ALPHA_OUT_INTERPOLATOR =
new PathInterpolator(0f, 0f, 0.8f, 1f);
@@ -71,6 +73,9 @@
private boolean mLastDark;
private boolean mDark;
private boolean mDelayTouchFeedback;
+ private boolean mSpeedUpNextFade;
+ // When non-null, this runs the next time this ripple is drawn invisibly.
+ private Runnable mOnInvisibleRunnable;
private final Interpolator mInterpolator = new LogInterpolator();
private boolean mSupportHardware;
@@ -112,6 +117,18 @@
mDelayTouchFeedback = delay;
}
+ /** Next time we fade out (pressed==false), use a shorter duration than the standard. */
+ public void speedUpNextFade() {
+ mSpeedUpNextFade = true;
+ }
+
+ /**
+ * @param onInvisibleRunnable run after we are next drawn invisibly. Only used once.
+ */
+ void setOnInvisibleRunnable(Runnable onInvisibleRunnable) {
+ mOnInvisibleRunnable = onInvisibleRunnable;
+ }
+
public void setType(Type type) {
mType = type;
}
@@ -161,6 +178,11 @@
} else {
drawSoftware(canvas);
}
+
+ if (!mPressed && !mVisible && mOnInvisibleRunnable != null) {
+ new Handler(Looper.getMainLooper()).post(mOnInvisibleRunnable);
+ mOnInvisibleRunnable = null;
+ }
}
@Override
@@ -270,7 +292,7 @@
return true;
}
- public void setPressed(boolean pressed) {
+ private void setPressed(boolean pressed) {
if (mDark != mLastDark && pressed) {
mRipplePaint = null;
mLastDark = mDark;
@@ -350,7 +372,7 @@
private void exitSoftware() {
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
alphaAnimator.setInterpolator(ALPHA_OUT_INTERPOLATOR);
- alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
+ alphaAnimator.setDuration(getFadeDuration());
alphaAnimator.addListener(mAnimatorListener);
alphaAnimator.start();
mRunningAnimations.add(alphaAnimator);
@@ -414,6 +436,12 @@
return Math.min(size, mMaxWidth);
}
+ private int getFadeDuration() {
+ int duration = mSpeedUpNextFade ? ANIMATION_DURATION_FADE_FAST : ANIMATION_DURATION_FADE;
+ mSpeedUpNextFade = false;
+ return duration;
+ }
+
private void enterHardware() {
endAnimations("enterHardware", true /* cancel */);
mVisible = true;
@@ -471,7 +499,7 @@
mPaintProp = CanvasProperty.createPaint(getRipplePaint());
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
RenderNodeAnimator.PAINT_ALPHA, 0);
- opacityAnim.setDuration(ANIMATION_DURATION_FADE);
+ opacityAnim.setDuration(getFadeDuration());
opacityAnim.setInterpolator(ALPHA_OUT_INTERPOLATOR);
opacityAnim.addListener(mAnimatorListener);
opacityAnim.addListener(mExitHwTraceAnimator);
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index 5de370f..7733841 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -28,7 +28,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -54,21 +53,20 @@
/** View Controller for {@link com.android.keyguard.EmergencyButton}. */
@KeyguardBouncerScope
public class EmergencyButtonController extends ViewController<EmergencyButton> {
- static final String LOG_TAG = "EmergencyButton";
+ private static final String TAG = "EmergencyButton";
private final ConfigurationController mConfigurationController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final TelephonyManager mTelephonyManager;
private final PowerManager mPowerManager;
private final ActivityTaskManager mActivityTaskManager;
- private ShadeController mShadeController;
+ private final ShadeController mShadeController;
private final TelecomManager mTelecomManager;
private final MetricsLogger mMetricsLogger;
private EmergencyButtonCallback mEmergencyButtonCallback;
- private LockPatternUtils mLockPatternUtils;
- private Executor mMainExecutor;
- private Executor mBackgroundExecutor;
- private SelectedUserInteractor mSelectedUserInteractor;
+ private final LockPatternUtils mLockPatternUtils;
+ private final Executor mMainExecutor;
+ private final Executor mBackgroundExecutor;
+ private final SelectedUserInteractor mSelectedUserInteractor;
private final KeyguardUpdateMonitorCallback mInfoCallback =
new KeyguardUpdateMonitorCallback() {
@@ -93,17 +91,18 @@
@VisibleForTesting
public EmergencyButtonController(@Nullable EmergencyButton view,
ConfigurationController configurationController,
- KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager,
- PowerManager powerManager, ActivityTaskManager activityTaskManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ PowerManager powerManager,
+ ActivityTaskManager activityTaskManager,
ShadeController shadeController,
- @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
+ @Nullable TelecomManager telecomManager,
+ MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
Executor mainExecutor, Executor backgroundExecutor,
SelectedUserInteractor selectedUserInteractor) {
super(view);
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mTelephonyManager = telephonyManager;
mPowerManager = powerManager;
mActivityTaskManager = activityTaskManager;
mShadeController = shadeController;
@@ -183,7 +182,7 @@
} else {
mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */);
if (mTelecomManager == null) {
- Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer");
+ Log.wtf(TAG, "TelecomManager was null, cannot launch emergency dialer");
return;
}
Intent emergencyDialIntent =
@@ -212,10 +211,9 @@
public static class Factory {
private final ConfigurationController mConfigurationController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final TelephonyManager mTelephonyManager;
private final PowerManager mPowerManager;
private final ActivityTaskManager mActivityTaskManager;
- private ShadeController mShadeController;
+ private final ShadeController mShadeController;
@Nullable
private final TelecomManager mTelecomManager;
private final MetricsLogger mMetricsLogger;
@@ -226,10 +224,12 @@
@Inject
public Factory(ConfigurationController configurationController,
- KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager,
- PowerManager powerManager, ActivityTaskManager activityTaskManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ PowerManager powerManager,
+ ActivityTaskManager activityTaskManager,
ShadeController shadeController,
- @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
+ @Nullable TelecomManager telecomManager,
+ MetricsLogger metricsLogger,
LockPatternUtils lockPatternUtils,
@Main Executor mainExecutor,
@Background Executor backgroundExecutor,
@@ -237,7 +237,6 @@
mConfigurationController = configurationController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mTelephonyManager = telephonyManager;
mPowerManager = powerManager;
mActivityTaskManager = activityTaskManager;
mShadeController = shadeController;
@@ -252,9 +251,9 @@
/** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
public EmergencyButtonController create(EmergencyButton view) {
return new EmergencyButtonController(view, mConfigurationController,
- mKeyguardUpdateMonitor, mTelephonyManager, mPowerManager, mActivityTaskManager,
- mShadeController, mTelecomManager, mMetricsLogger, mLockPatternUtils,
- mMainExecutor, mBackgroundExecutor, mSelectedUserInteractor);
+ mKeyguardUpdateMonitor, mPowerManager, mActivityTaskManager, mShadeController,
+ mTelecomManager, mMetricsLogger, mLockPatternUtils, mMainExecutor,
+ mBackgroundExecutor, mSelectedUserInteractor);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index f9eb686..6b8009d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -47,6 +47,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder;
+import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
@@ -123,6 +124,7 @@
private View mSmartspaceView;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ private final InWindowLauncherUnlockAnimationManager mInWindowLauncherUnlockAnimationManager;
private boolean mShownOnSecondaryDisplay = false;
private boolean mOnlyClock = false;
@@ -190,7 +192,8 @@
AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
KeyguardInteractor keyguardInteractor,
KeyguardClockInteractor keyguardClockInteractor,
- FeatureFlagsClassic featureFlags) {
+ FeatureFlagsClassic featureFlags,
+ InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mClockRegistry = clockRegistry;
@@ -214,6 +217,7 @@
mFeatureFlags = featureFlags;
mKeyguardInteractor = keyguardInteractor;
mKeyguardClockInteractor = keyguardClockInteractor;
+ mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager;
mClockChangedListener = new ClockRegistry.ClockChangeListener() {
@Override
@@ -438,6 +442,8 @@
mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
mKeyguardUnlockAnimationController.setLockscreenSmartspace(mSmartspaceView);
+ mInWindowLauncherUnlockAnimationManager.setLockscreenSmartspace(mSmartspaceView);
+
mView.setSmartspace(mSmartspaceView);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 181aa5c..f091558 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -22,6 +22,7 @@
import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
+import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
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;
@@ -459,7 +460,7 @@
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ if (keyguardBottomAreaRefactor()) {
mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
scaledPadding);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/accessibility/TEST_MAPPING
index 055fad1..be26b43 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/TEST_MAPPING
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/TEST_MAPPING
@@ -5,6 +5,18 @@
"options": [
{
"include-filter": "com.android.systemui.accessibility"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java
index 72935f7..d92b506 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java
@@ -18,4 +18,8 @@
interface IRadiiAnimationListener {
void onRadiiAnimationUpdate(float[] radii);
+
+ void onRadiiAnimationStart();
+
+ void onRadiiAnimationStop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 761551c..34d7cec 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -94,7 +94,19 @@
mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS);
mFadeOutAnimator.addUpdateListener(
(animation) -> menuView.setAlpha((float) animation.getAnimatedValue()));
- mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), mMenuView::setRadii);
+ mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(),
+ new IRadiiAnimationListener() {
+ @Override
+ public void onRadiiAnimationUpdate(float[] radii) {
+ mMenuView.setRadii(radii);
+ }
+
+ @Override
+ public void onRadiiAnimationStart() {}
+
+ @Override
+ public void onRadiiAnimationStop() {}
+ });
}
void moveToPosition(PointF position) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java
index acad36e..4aa0d89 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java
@@ -55,15 +55,19 @@
@Override
public void onAnimationStart(@NonNull Animator animation) {
animationListener.onRadiiAnimationUpdate(evaluate(/* t = */ 0.0f));
+ animationListener.onRadiiAnimationStart();
}
@Override
- public void onAnimationEnd(@NonNull Animator animation) {}
+ public void onAnimationEnd(@NonNull Animator animation) {
+ animationListener.onRadiiAnimationStop();
+ }
@Override
public void onAnimationCancel(@NonNull Animator animation) {
animationListener.onRadiiAnimationUpdate(
evaluate(mAnimationDriver.getAnimatedFraction()));
+ animationListener.onRadiiAnimationStop();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
new file mode 100644
index 0000000..bba0050
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.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.systemui.bouncer.data.repository
+
+import android.content.res.Resources
+import com.android.internal.R
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** Encapsulates Emergency Services related state. */
+@SysUISingleton
+class EmergencyServicesRepository
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Main private val resources: Resources,
+ configurationRepository: ConfigurationRepository,
+) {
+ /**
+ * Whether to enable emergency services calls while the SIM card is locked. This is disabled in
+ * certain countries that don't support this.
+ */
+ val enableEmergencyCallWhileSimLocked: StateFlow<Boolean> =
+ configurationRepository.onConfigurationChange
+ .map { getEnableEmergencyCallWhileSimLocked() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = getEnableEmergencyCallWhileSimLocked()
+ )
+
+ private fun getEnableEmergencyCallWhileSimLocked(): Boolean {
+ return resources.getBoolean(R.bool.config_enable_emergency_call_while_sim_locked)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
new file mode 100644
index 0000000..f36ef66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.bouncer.domain.interactor
+
+import android.annotation.SuppressLint
+import android.app.ActivityOptions
+import android.app.ActivityTaskManager
+import android.content.Context
+import android.content.Intent
+import android.os.UserHandle
+import android.telecom.TelecomManager
+import com.android.internal.R
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent
+import com.android.internal.util.EmergencyAffordanceManager
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.doze.DozeLogger
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.EmergencyDialerConstants
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.withContext
+
+/**
+ * Encapsulates business logic and application state for the bouncer action button. The action
+ * button can support multiple different actions, depending on device state.
+ */
+@SysUISingleton
+class BouncerActionButtonInteractor
+@Inject
+constructor(
+ @Application private val applicationContext: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val repository: EmergencyServicesRepository,
+ // TODO(b/307977401): Replace with `MobileConnectionsInteractor` when available.
+ private val mobileConnectionsRepository: MobileConnectionsRepository,
+ private val telephonyInteractor: TelephonyInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val activityTaskManager: ActivityTaskManager,
+ private val telecomManager: TelecomManager?,
+ private val emergencyAffordanceManager: EmergencyAffordanceManager,
+ private val emergencyDialerIntentFactory: EmergencyDialerIntentFactory,
+ private val metricsLogger: MetricsLogger,
+ private val dozeLogger: DozeLogger,
+) {
+ /** The bouncer action button. If `null`, the button should not be shown. */
+ val actionButton: Flow<BouncerActionButtonModel?> =
+ if (telecomManager == null || !telephonyInteractor.hasTelephonyRadio) {
+ flowOf(null)
+ } else {
+ merge(
+ telephonyInteractor.isInCall.asUnitFlow,
+ mobileConnectionsRepository.isAnySimSecure.asUnitFlow,
+ authenticationInteractor.authenticationMethod.asUnitFlow,
+ repository.enableEmergencyCallWhileSimLocked.asUnitFlow,
+ )
+ .map {
+ when {
+ isReturnToCallButton() -> returnToCallButtonModel
+ isEmergencyCallButton() -> emergencyCallButtonModel
+ else -> null // Do not show the button.
+ }
+ }
+ .distinctUntilChanged()
+ }
+
+ private val returnToCallButtonModel: BouncerActionButtonModel by lazy {
+ BouncerActionButtonModel(
+ label = applicationContext.getString(R.string.lockscreen_return_to_call),
+ onClick = {
+ prepareToPerformAction()
+ returnToCall()
+ },
+ onLongClick = null
+ )
+ }
+
+ private val emergencyCallButtonModel: BouncerActionButtonModel by lazy {
+ BouncerActionButtonModel(
+ label = applicationContext.getString(R.string.lockscreen_emergency_call),
+ onClick = {
+ prepareToPerformAction()
+ dozeLogger.logEmergencyCall()
+ startEmergencyDialerActivity()
+ },
+ // TODO(b/308001302): The long click detector doesn't work properly, investigate.
+ onLongClick = {
+ if (emergencyAffordanceManager.needsEmergencyAffordance()) {
+ prepareToPerformAction()
+
+ // TODO(b/298026988): Check that !longPressWasDragged before invoking.
+ emergencyAffordanceManager.performEmergencyCall()
+ }
+ }
+ )
+ }
+
+ private fun startEmergencyDialerActivity() {
+ emergencyDialerIntentFactory()?.apply {
+ flags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or
+ Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS or
+ Intent.FLAG_ACTIVITY_CLEAR_TOP
+
+ putExtra(
+ EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON,
+ )
+
+ // TODO(b/25189994): Use the ActivityStarter interface instead.
+ applicationContext.startActivityAsUser(
+ this,
+ ActivityOptions.makeCustomAnimation(applicationContext, 0, 0).toBundle(),
+ UserHandle(selectedUserInteractor.getSelectedUserId())
+ )
+ }
+ }
+
+ private fun isReturnToCallButton() = telephonyInteractor.isInCall.value
+
+ private suspend fun isEmergencyCallButton(): Boolean {
+ return if (mobileConnectionsRepository.getIsAnySimSecure()) {
+ // Some countries can't handle emergency calls while SIM is locked.
+ repository.enableEmergencyCallWhileSimLocked.value
+ } else {
+ // Only show if there is a secure screen (password/pin/pattern/SIM pin/SIM puk).
+ withContext(backgroundDispatcher) {
+ authenticationInteractor.getAuthenticationMethod().isSecure
+ }
+ }
+ }
+
+ private fun prepareToPerformAction() {
+ // TODO(b/308001302): Trigger occlusion and resetting bouncer state.
+ metricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL)
+ activityTaskManager.stopSystemLockTaskMode()
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun returnToCall() {
+ telecomManager?.showInCallScreen(/* showDialpad = */ false)
+ }
+
+ private val <T> Flow<T>.asUnitFlow: Flow<Unit>
+ get() = map {}
+}
+
+/**
+ * Creates an intent to launch the Emergency Services dialer. If no [TelecomManager] is present,
+ * returns `null`.
+ */
+interface EmergencyDialerIntentFactory {
+ operator fun invoke(): Intent?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
new file mode 100644
index 0000000..e398c93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorModule.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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.bouncer.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.telecom.TelecomManager
+import com.android.internal.util.EmergencyAffordanceManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import dagger.Module
+import dagger.Provides
+
+/** Module for providing interactor-related objects for the bouncer. */
+@Module
+object BouncerInteractorModule {
+
+ @Provides
+ fun emergencyDialerIntentFactory(
+ telecomManager: TelecomManager?
+ ): EmergencyDialerIntentFactory {
+ return object : EmergencyDialerIntentFactory {
+ override fun invoke(): Intent? {
+ return telecomManager?.createLaunchEmergencyDialerIntent(/* number = */ null)
+ }
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ fun emergencyAffordanceManager(
+ @Application applicationContext: Context,
+ ): EmergencyAffordanceManager {
+ return EmergencyAffordanceManager(applicationContext)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt
new file mode 100644
index 0000000..7f1730c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerActionButtonModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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.bouncer.shared.model
+
+/** Models the action button on the bouncer. */
+data class BouncerActionButtonModel(
+ /** The text to be shown on the button. */
+ val label: String,
+
+ /** The action to perform when the user clicks on the button. */
+ val onClick: () -> Unit,
+
+ /**
+ * The action to perform when the user long-clicks on the button. When not provided, long-clicks
+ * will be treated as regular clicks.
+ */
+ val onLongClick: (() -> Unit)? = null,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index ef0609a..0f77724 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -21,14 +21,15 @@
import androidx.core.graphics.drawable.toBitmap
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
import com.android.systemui.user.ui.viewmodel.UserViewModel
@@ -59,10 +60,10 @@
private val bouncerInteractor: BouncerInteractor,
authenticationInteractor: AuthenticationInteractor,
flags: SceneContainerFlags,
- private val telephonyInteractor: TelephonyInteractor,
selectedUser: Flow<UserViewModel>,
users: Flow<List<UserViewModel>>,
userSwitcherMenu: Flow<List<UserActionViewModel>>,
+ actionButtonInteractor: BouncerActionButtonInteractor,
) {
val selectedUserImage: StateFlow<Bitmap?> =
selectedUser
@@ -150,8 +151,16 @@
),
)
- val isEmergencyButtonVisible: Boolean
- get() = telephonyInteractor.hasTelephonyRadio
+ /**
+ * The bouncer action button (Return to Call / Emergency Call). If `null`, the button should not
+ * be shown.
+ */
+ val actionButton: StateFlow<BouncerActionButtonModel?> =
+ actionButtonInteractor.actionButton.stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null
+ )
init {
if (flags.isEnabled()) {
@@ -176,11 +185,6 @@
}
}
- /** Notifies that the emergency services button was clicked. */
- fun onEmergencyServicesButtonClicked() {
- // TODO(b/280877228): implement this
- }
-
/** Notifies that a throttling dialog has been dismissed by the user. */
fun onThrottlingDialogDismissed() {
_throttlingDialogMessage.value = null
@@ -271,8 +275,8 @@
bouncerInteractor: BouncerInteractor,
authenticationInteractor: AuthenticationInteractor,
flags: SceneContainerFlags,
- telephonyInteractor: TelephonyInteractor,
userSwitcherViewModel: UserSwitcherViewModel,
+ actionButtonInteractor: BouncerActionButtonInteractor,
): BouncerViewModel {
return BouncerViewModel(
applicationContext = applicationContext,
@@ -281,10 +285,10 @@
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
flags = flags,
- telephonyInteractor = telephonyInteractor,
selectedUser = userSwitcherViewModel.selectedUser,
users = userSwitcherViewModel.users,
userSwitcherMenu = userSwitcherViewModel.menu,
+ actionButtonInteractor = actionButtonInteractor,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index e449274..7fa762a 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -44,11 +44,15 @@
interface ConfigurationRepository {
/** Called whenever ui mode, theme or configuration has changed. */
val onAnyConfigurationChange: Flow<Unit>
+
+ /** Called whenever the configuration has changed. */
+ val onConfigurationChange: Flow<Unit>
+
val scaleForResolution: Flow<Float>
fun getResolutionScale(): Float
- /** Convience to context.resources.getDimensionPixelSize() */
+ /** Convenience to context.resources.getDimensionPixelSize() */
fun getDimensionPixelSize(id: Int): Int
}
@@ -87,7 +91,7 @@
awaitClose { configurationController.removeCallback(callback) }
}
- private val configurationChange: Flow<Unit> =
+ override val onConfigurationChange: Flow<Unit> =
ConflatedCallbackFlow.conflatedCallbackFlow {
val callback =
object : ConfigurationController.ConfigurationListener {
@@ -100,7 +104,7 @@
}
override val scaleForResolution: StateFlow<Float> =
- configurationChange
+ onConfigurationChange
.mapLatest { getResolutionScale() }
.distinctUntilChanged()
.stateIn(scope, SharingStarted.WhileSubscribed(), getResolutionScale())
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 b8e2de4..273adcf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.communal.dagger
+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
@@ -25,8 +27,10 @@
includes =
[
CommunalRepositoryModule::class,
+ CommunalMediaRepositoryModule::class,
CommunalTutorialRepositoryModule::class,
CommunalWidgetRepositoryModule::class,
+ CommunalDatabaseModule::class,
]
)
class CommunalModule
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
new file mode 100644
index 0000000..595d320
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.data.db
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+
+@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 1)
+abstract class CommunalDatabase : RoomDatabase() {
+ abstract fun communalWidgetDao(): CommunalWidgetDao
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabaseModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabaseModule.kt
new file mode 100644
index 0000000..e766290
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabaseModule.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.systemui.communal.data.db
+
+import android.content.Context
+import androidx.room.Room
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface CommunalDatabaseModule {
+ companion object {
+ @SysUISingleton
+ @Provides
+ fun provideCommunalDatabase(
+ @Application context: Context,
+ defaultWidgetPopulation: DefaultWidgetPopulation,
+ ): CommunalDatabase {
+ return Room.databaseBuilder(
+ context,
+ CommunalDatabase::class.java,
+ context.resources.getString(R.string.config_communalDatabase)
+ )
+ .fallbackToDestructiveMigration()
+ .addCallback(defaultWidgetPopulation)
+ .build()
+ }
+
+ @SysUISingleton
+ @Provides
+ fun provideCommunalWidgetDao(database: CommunalDatabase): CommunalWidgetDao =
+ database.communalWidgetDao()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
new file mode 100644
index 0000000..0d5336a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.data.db
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "communal_widget_table")
+data class CommunalWidgetItem(
+ @PrimaryKey(autoGenerate = true) val uid: Long,
+ /** Id of an app widget */
+ @ColumnInfo(name = "widget_id") val widgetId: Int,
+ /** Component name of the app widget provider */
+ @ColumnInfo(name = "component_name") val componentName: String,
+ /** Reference the id of an item persisted in the glanceable hub */
+ @ColumnInfo(name = "item_id") val itemId: Long,
+)
+
+@Entity(tableName = "communal_item_rank_table")
+data class CommunalItemRank(
+ /** Unique id of an item persisted in the glanceable hub */
+ @PrimaryKey(autoGenerate = true) val uid: Long,
+ /** Order in which the item will be displayed */
+ @ColumnInfo(name = "rank", defaultValue = "0") val rank: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
new file mode 100644
index 0000000..e50850d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.data.db
+
+import android.content.ComponentName
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Query
+import androidx.room.RoomDatabase
+import androidx.room.Transaction
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule.Companion.DEFAULT_WIDGETS
+import com.android.systemui.communal.shared.CommunalWidgetHost
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+import javax.inject.Named
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Callback that will be invoked when the Room database is created. Then the database will be
+ * populated with pre-configured default widgets to be rendered in the glanceable hub.
+ */
+class DefaultWidgetPopulation
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ private val communalWidgetHost: CommunalWidgetHost,
+ private val communalWidgetDaoProvider: Provider<CommunalWidgetDao>,
+ @Named(DEFAULT_WIDGETS) private val defaultWidgets: Array<String>,
+ @CommunalLog logBuffer: LogBuffer,
+) : RoomDatabase.Callback() {
+ companion object {
+ private const val TAG = "DefaultWidgetPopulation"
+ }
+ private val logger = Logger(logBuffer, TAG)
+
+ override fun onCreate(db: SupportSQLiteDatabase) {
+ super.onCreate(db)
+ applicationScope.launch {
+ addDefaultWidgets()
+ logger.i("Default widgets were populated in the database.")
+ }
+ }
+
+ // Read default widgets from config.xml and populate the database.
+ private suspend fun addDefaultWidgets() =
+ withContext(bgDispatcher) {
+ defaultWidgets.forEachIndexed { index, name ->
+ val provider = ComponentName.unflattenFromString(name)
+ provider?.let {
+ val id = communalWidgetHost.allocateIdAndBindWidget(provider)
+ id?.let {
+ communalWidgetDaoProvider
+ .get()
+ .addWidget(
+ widgetId = id,
+ provider = provider,
+ priority = defaultWidgets.size - index
+ )
+ }
+ }
+ }
+ }
+}
+
+@Dao
+interface CommunalWidgetDao {
+ @Query(
+ "SELECT * FROM communal_widget_table JOIN communal_item_rank_table " +
+ "ON communal_item_rank_table.uid = communal_widget_table.item_id"
+ )
+ fun getWidgets(): Flow<Map<CommunalItemRank, CommunalWidgetItem>>
+
+ @Query("SELECT * FROM communal_widget_table WHERE widget_id = :id")
+ fun getWidgetByIdNow(id: Int): CommunalWidgetItem
+
+ @Delete fun deleteWidgets(vararg widgets: CommunalWidgetItem)
+
+ @Query("DELETE FROM communal_item_rank_table WHERE uid = :itemId")
+ fun deleteItemRankById(itemId: Long)
+
+ @Query(
+ "INSERT INTO communal_widget_table(widget_id, component_name, item_id) " +
+ "VALUES(:widgetId, :componentName, :itemId)"
+ )
+ fun insertWidget(widgetId: Int, componentName: String, itemId: Long): Long
+
+ @Query("INSERT INTO communal_item_rank_table(rank) VALUES(:rank)")
+ fun insertItemRank(rank: Int): Long
+
+ @Transaction
+ fun addWidget(widgetId: Int, provider: ComponentName, priority: Int): Long {
+ return insertWidget(
+ widgetId = widgetId,
+ componentName = provider.flattenToString(),
+ insertItemRank(priority),
+ )
+ }
+
+ @Transaction
+ fun deleteWidgetById(widgetId: Int) {
+ val widget = getWidgetByIdNow(widgetId)
+ deleteItemRankById(widget.itemId)
+ deleteWidgets(widget)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
deleted file mode 100644
index 1a214ba..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetMetadata.kt
+++ /dev/null
@@ -1,31 +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.systemui.communal.data.model
-
-import com.android.systemui.communal.shared.model.CommunalContentSize
-
-/** Metadata for the default widgets */
-data class CommunalWidgetMetadata(
- /* Widget provider component name */
- val componentName: String,
-
- /* Defines the order in which the widget will be rendered in the grid. */
- val priority: Int,
-
- /* Supported sizes */
- val sizes: List<CommunalContentSize>
-)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
new file mode 100644
index 0000000..e41c322
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
+
+/** Encapsulates the state of smartspace in communal. */
+interface CommunalMediaRepository {
+ val mediaPlaying: Flow<Boolean>
+}
+
+@SysUISingleton
+class CommunalMediaRepositoryImpl
+@Inject
+constructor(
+ private val mediaDataManager: MediaDataManager,
+) : CommunalMediaRepository {
+
+ private val mediaDataListener =
+ object : MediaDataManager.Listener {
+ override fun onMediaDataLoaded(
+ key: String,
+ oldKey: String?,
+ data: MediaData,
+ immediately: Boolean,
+ receivedSmartspaceCardLatency: Int,
+ isSsReactivated: Boolean
+ ) {
+ if (!mediaDataManager.hasAnyMediaOrRecommendation()) {
+ return
+ }
+ _mediaPlaying.value = true
+ }
+
+ override fun onMediaDataRemoved(key: String) {
+ if (mediaDataManager.hasAnyMediaOrRecommendation()) {
+ return
+ }
+ _mediaPlaying.value = false
+ }
+ }
+
+ private val _mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override val mediaPlaying: Flow<Boolean> =
+ _mediaPlaying
+ .onStart {
+ mediaDataManager.addListener(mediaDataListener)
+ _mediaPlaying.value = mediaDataManager.hasAnyMediaOrRecommendation()
+ }
+ .onCompletion { mediaDataManager.removeListener(mediaDataListener) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt
new file mode 100644
index 0000000..2c6d9e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalMediaRepositoryModule {
+ @Binds fun communalMediaRepository(impl: CommunalMediaRepositoryImpl): CommunalMediaRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 5c4ee35..3119b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.data.repository
import com.android.systemui.Flags.communalHub
@@ -5,16 +21,24 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
/** Encapsulates the state of communal mode. */
interface CommunalRepository {
/** Whether communal features are enabled. */
val isCommunalEnabled: Boolean
+ /** Whether the communal hub is showing. */
+ val isCommunalHubShowing: Flow<Boolean>
+
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [setDesiredScene].
@@ -30,6 +54,8 @@
@Inject
constructor(
private val featureFlagsClassic: FeatureFlagsClassic,
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerRepository: SceneContainerRepository,
) : CommunalRepository {
override val isCommunalEnabled: Boolean
get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
@@ -41,4 +67,11 @@
override fun setDesiredScene(desiredScene: CommunalSceneKey) {
_desiredScene.value = desiredScene
}
+
+ override val isCommunalHubShowing: Flow<Boolean> =
+ if (sceneContainerFlags.isEnabled()) {
+ sceneContainerRepository.desiredScene.map { scene -> scene.key == SceneKey.Communal }
+ } else {
+ desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
index 9d95b9e..1de3459 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepositoryModule.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.data.repository
import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index 77025dc..6b27ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -28,47 +28,62 @@
import android.os.UserManager
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.communal.data.model.CommunalWidgetMetadata
+import com.android.systemui.communal.data.db.CommunalItemRank
+import com.android.systemui.communal.data.db.CommunalWidgetDao
+import com.android.systemui.communal.data.db.CommunalWidgetItem
+import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
-import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
/** Encapsulates the state of widgets for communal mode. */
interface CommunalWidgetRepository {
/** A flow of provider info for the stopwatch widget, or null if widget is unavailable. */
val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?>
- /** Widgets that are allowed to render in the glanceable hub */
- val communalWidgetAllowlist: List<CommunalWidgetMetadata>
-
- /** A flow of information about all the communal widgets to show. */
+ /** A flow of information about active communal widgets stored in database. */
val communalWidgets: Flow<List<CommunalWidgetContentModel>>
+
+ /** Add a widget at the specified position in the app widget service and the database. */
+ fun addWidget(provider: ComponentName, priority: Int) {}
+
+ /** Delete a widget by id from app widget service and the database. */
+ fun deleteWidget(widgetId: Int) {}
}
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class CommunalWidgetRepositoryImpl
@Inject
constructor(
- @Application private val applicationContext: Context,
private val appWidgetManager: AppWidgetManager,
private val appWidgetHost: AppWidgetHost,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
broadcastDispatcher: BroadcastDispatcher,
communalRepository: CommunalRepository,
+ private val communalWidgetHost: CommunalWidgetHost,
+ private val communalWidgetDao: CommunalWidgetDao,
private val packageManager: PackageManager,
private val userManager: UserManager,
private val userTracker: UserTracker,
@@ -79,18 +94,12 @@
const val TAG = "CommunalWidgetRepository"
const val WIDGET_LABEL = "Stopwatch"
}
- override val communalWidgetAllowlist: List<CommunalWidgetMetadata>
private val logger = Logger(logBuffer, TAG)
// Whether the [AppWidgetHost] is listening for updates.
private var isHostListening = false
- init {
- communalWidgetAllowlist =
- if (communalRepository.isCommunalEnabled) getWidgetAllowlist() else emptyList()
- }
-
// Widgets that should be rendered in communal mode.
private val widgets: HashMap<Int, CommunalAppWidgetInfo> = hashMapOf()
@@ -136,7 +145,6 @@
true
} else {
stopListening()
- clearWidgets()
false
}
}
@@ -157,57 +165,50 @@
return@map null
}
- return@map addWidget(providerInfo)
+ return@map addStopWatchWidget(providerInfo)
}
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
- isHostActive.map { isHostActive ->
+ isHostActive.flatMapLatest { isHostActive ->
if (!isHostActive) {
- return@map emptyList()
+ return@flatMapLatest flowOf(emptyList())
}
-
- // The allowlist should be fetched from the local database with all the metadata tied to
- // a widget, including an appWidgetId if it has been bound. Before the database is set
- // up, we are going to use the app widget host as the source of truth for bound widgets,
- // and rebind each time on boot.
-
- // Remove all previously bound widgets.
- appWidgetHost.appWidgetIds.forEach { appWidgetHost.deleteAppWidgetId(it) }
-
- val inventory = mutableListOf<CommunalWidgetContentModel>()
-
- // Bind all widgets from the allowlist.
- communalWidgetAllowlist.forEach {
- val id = appWidgetHost.allocateAppWidgetId()
- appWidgetManager.bindAppWidgetId(
- id,
- ComponentName.unflattenFromString(it.componentName),
- )
-
- inventory.add(
- CommunalWidgetContentModel(
- appWidgetId = id,
- providerInfo = appWidgetManager.getAppWidgetInfo(id),
- priority = it.priority,
- )
- )
- }
-
- return@map inventory.toList()
+ communalWidgetDao.getWidgets().map { it.map(::mapToContentModel) }
}
- private fun getWidgetAllowlist(): List<CommunalWidgetMetadata> {
- val componentNames =
- applicationContext.resources.getStringArray(R.array.config_communalWidgetAllowlist)
- return componentNames.mapIndexed { index, name ->
- CommunalWidgetMetadata(
- componentName = name,
- priority = componentNames.size - index,
- sizes = listOf(CommunalContentSize.HALF),
- )
+ override fun addWidget(provider: ComponentName, priority: Int) {
+ applicationScope.launch(bgDispatcher) {
+ val id = communalWidgetHost.allocateIdAndBindWidget(provider)
+ id?.let {
+ communalWidgetDao.addWidget(
+ widgetId = it,
+ provider = provider,
+ priority = priority,
+ )
+ }
+ logger.i("Added widget ${provider.flattenToString()} at position $priority.")
}
}
+ override fun deleteWidget(widgetId: Int) {
+ applicationScope.launch(bgDispatcher) {
+ communalWidgetDao.deleteWidgetById(widgetId)
+ appWidgetHost.deleteAppWidgetId(widgetId)
+ logger.i("Deleted widget with id $widgetId.")
+ }
+ }
+
+ private fun mapToContentModel(
+ entry: Map.Entry<CommunalItemRank, CommunalWidgetItem>
+ ): CommunalWidgetContentModel {
+ val (_, widgetId) = entry.value
+ return CommunalWidgetContentModel(
+ appWidgetId = widgetId,
+ providerInfo = appWidgetManager.getAppWidgetInfo(widgetId),
+ priority = entry.key.rank,
+ )
+ }
+
private fun startListening() {
if (isHostListening) {
return
@@ -226,7 +227,8 @@
isHostListening = false
}
- private fun addWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
+ // TODO(b/306471933): remove this prototype that shows a stopwatch in the communal blueprint
+ private fun addStopWatchWidget(providerInfo: AppWidgetProviderInfo): CommunalAppWidgetInfo {
val existing = widgets.values.firstOrNull { it.providerInfo == providerInfo }
if (existing != null) {
return existing
@@ -241,9 +243,4 @@
widgets[appWidgetId] = widget
return widget
}
-
- private fun clearWidgets() {
- widgets.keys.forEach { appWidgetId -> appWidgetHost.deleteAppWidgetId(appWidgetId) }
- widgets.clear()
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index 3d1185b..5793f10 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -20,16 +20,24 @@
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetManager
import android.content.Context
+import android.content.res.Resources
+import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
import dagger.Provides
+import javax.inject.Named
@Module
interface CommunalWidgetRepositoryModule {
companion object {
private const val APP_WIDGET_HOST_ID = 116
+ const val DEFAULT_WIDGETS = "default_widgets"
@SysUISingleton
@Provides
@@ -42,6 +50,22 @@
fun provideAppWidgetHost(@Application context: Context): AppWidgetHost {
return AppWidgetHost(context, APP_WIDGET_HOST_ID)
}
+
+ @SysUISingleton
+ @Provides
+ fun provideCommunalWidgetHost(
+ appWidgetManager: AppWidgetManager,
+ appWidgetHost: AppWidgetHost,
+ @CommunalLog logBuffer: LogBuffer,
+ ): CommunalWidgetHost {
+ return CommunalWidgetHost(appWidgetManager, appWidgetHost, logBuffer)
+ }
+
+ @Provides
+ @Named(DEFAULT_WIDGETS)
+ fun provideDefaultWidgets(@Main resources: Resources): Array<String> {
+ return resources.getStringArray(R.array.config_communalWidgetAllowlist)
+ }
}
@Binds
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 ccccbb6..eb36b19 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
@@ -16,24 +16,39 @@
package com.android.systemui.communal.domain.interactor
+import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetHost
+import android.content.ComponentName
+import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
+import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
/** Encapsulates business-logic related to communal mode. */
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class CommunalInteractor
@Inject
constructor(
private val communalRepository: CommunalRepository,
- widgetRepository: CommunalWidgetRepository,
+ private val widgetRepository: CommunalWidgetRepository,
+ mediaRepository: CommunalMediaRepository,
+ smartspaceRepository: SmartspaceRepository,
+ tutorialInteractor: CommunalTutorialInteractor,
+ private val appWidgetHost: AppWidgetHost,
) {
/** Whether communal features are enabled. */
@@ -44,14 +59,6 @@
val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
/**
- * A flow of information about widgets to be shown in communal hub.
- *
- * Currently only showing persistent widgets that have been bound to the app widget service
- * (have an allocated id).
- */
- val widgetContent: Flow<List<CommunalWidgetContentModel>> = widgetRepository.communalWidgets
-
- /**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [onSceneChanged].
*/
@@ -68,4 +75,79 @@
fun onSceneChanged(newScene: CommunalSceneKey) {
communalRepository.setDesiredScene(newScene)
}
+
+ /** Add a widget at the specified position. */
+ fun addWidget(componentName: ComponentName, priority: Int) =
+ widgetRepository.addWidget(componentName, priority)
+
+ /** Delete a widget by id. */
+ fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
+
+ /** A list of all the communal content to be displayed in the communal hub. */
+ @OptIn(ExperimentalCoroutinesApi::class)
+ val communalContent: Flow<List<CommunalContentModel>> =
+ tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode ->
+ if (isTutorialMode) {
+ return@flatMapLatest flowOf(tutorialContent)
+ }
+ combine(smartspaceContent, umoContent, widgetContent) { smartspace, umo, widgets ->
+ smartspace + umo + widgets
+ }
+ }
+
+ /** A list of widget content to be displayed in the communal hub. */
+ private val widgetContent: Flow<List<CommunalContentModel.Widget>> =
+ widgetRepository.communalWidgets.map { widgets ->
+ widgets.map Widget@{ widget ->
+ return@Widget CommunalContentModel.Widget(
+ appWidgetId = widget.appWidgetId,
+ providerInfo = widget.providerInfo,
+ appWidgetHost = appWidgetHost,
+ )
+ }
+ }
+
+ /** A flow of available smartspace content. Currently only showing timer targets. */
+ private val smartspaceContent: Flow<List<CommunalContentModel.Smartspace>> =
+ if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
+ flowOf(emptyList())
+ } else {
+ smartspaceRepository.lockscreenSmartspaceTargets.map { targets ->
+ targets
+ .filter { target ->
+ target.featureType == SmartspaceTarget.FEATURE_TIMER &&
+ target.remoteViews != null
+ }
+ .map Target@{ target ->
+ return@Target CommunalContentModel.Smartspace(
+ smartspaceTargetId = target.smartspaceTargetId,
+ remoteViews = target.remoteViews!!,
+ // Smartspace always as HALF for now.
+ size = CommunalContentSize.HALF,
+ )
+ }
+ }
+ }
+
+ /** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
+ private val tutorialContent: List<CommunalContentModel.Tutorial> =
+ listOf(
+ CommunalContentModel.Tutorial(id = 0, CommunalContentSize.FULL),
+ CommunalContentModel.Tutorial(id = 1, CommunalContentSize.THIRD),
+ CommunalContentModel.Tutorial(id = 2, CommunalContentSize.THIRD),
+ CommunalContentModel.Tutorial(id = 3, CommunalContentSize.THIRD),
+ CommunalContentModel.Tutorial(id = 4, CommunalContentSize.HALF),
+ CommunalContentModel.Tutorial(id = 5, CommunalContentSize.HALF),
+ CommunalContentModel.Tutorial(id = 6, CommunalContentSize.HALF),
+ CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
+ )
+
+ private val umoContent: Flow<List<CommunalContentModel.Umo>> =
+ mediaRepository.mediaPlaying.flatMapLatest { mediaPlaying ->
+ if (mediaPlaying) {
+ flowOf(listOf(CommunalContentModel.Umo(CommunalContentSize.THIRD)))
+ } else {
+ flowOf(emptyList())
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 7f43eb5..5ca89f2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -17,13 +17,11 @@
package com.android.systemui.communal.domain.interactor
import android.provider.Settings
+import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalTutorialRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,9 +44,7 @@
@Application private val scope: CoroutineScope,
private val communalTutorialRepository: CommunalTutorialRepository,
keyguardInteractor: KeyguardInteractor,
- private val communalInteractor: CommunalInteractor,
- private val sceneContainerFlags: SceneContainerFlags,
- private val sceneInteractor: SceneInteractor,
+ private val communalRepository: CommunalRepository,
) {
/** An observable for whether the tutorial is available. */
val isTutorialAvailable: Flow<Boolean> =
@@ -74,17 +70,11 @@
if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
return@flatMapLatest flowOf(null)
}
- if (sceneContainerFlags.isEnabled()) {
- sceneInteractor.desiredScene.map { sceneModel ->
- nextStateAfterTransition(
- tutorialSettingState,
- sceneModel.key == SceneKey.Communal
- )
- }
- } else {
- communalInteractor.isCommunalShowing.map {
- nextStateAfterTransition(tutorialSettingState, it)
- }
+ communalRepository.isCommunalHubShowing.map { isCommunalShowing ->
+ nextStateAfterTransition(
+ tutorialSettingState,
+ isCommunalShowing,
+ )
}
}
.filterNotNull()
@@ -102,7 +92,7 @@
private var job: Job? = null
private fun listenForTransitionToUpdateTutorialState() {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalRepository.isCommunalEnabled) {
return
}
job =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
new file mode 100644
index 0000000..bb9b4b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.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.systemui.communal.domain.model
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetProviderInfo
+import android.widget.RemoteViews
+import com.android.systemui.communal.shared.model.CommunalContentSize
+
+/** Encapsulates data for a communal content. */
+sealed interface CommunalContentModel {
+ /** Unique key across all types of content models. */
+ val key: String
+
+ /** Size to be rendered in the grid. */
+ val size: CommunalContentSize
+
+ class Widget(
+ val appWidgetId: Int,
+ val providerInfo: AppWidgetProviderInfo,
+ val appWidgetHost: AppWidgetHost,
+ ) : CommunalContentModel {
+ override val key = "widget_$appWidgetId"
+ // Widget size is always half.
+ override val size = CommunalContentSize.HALF
+ }
+
+ class Tutorial(
+ id: Int,
+ override val size: CommunalContentSize,
+ ) : CommunalContentModel {
+ override val key = "tutorial_$id"
+ }
+
+ class Smartspace(
+ smartspaceTargetId: String,
+ val remoteViews: RemoteViews,
+ override val size: CommunalContentSize,
+ ) : CommunalContentModel {
+ override val key = "smartspace_$smartspaceTargetId"
+ }
+
+ class Umo(
+ override val size: CommunalContentSize,
+ ) : CommunalContentModel {
+ override val key = UMO_KEY
+ }
+
+ companion object {
+ /** Key for the [Umo] in CommunalContentModel. There should only ever be one UMO. */
+ const val UMO_KEY = "umo"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
new file mode 100644
index 0000000..086d729
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.shared
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+
+/**
+ * Widget host that interacts with AppWidget service and host to manage and provide info for widgets
+ * shown in the glanceable hub.
+ */
+class CommunalWidgetHost
+@Inject
+constructor(
+ private val appWidgetManager: AppWidgetManager,
+ private val appWidgetHost: AppWidgetHost,
+ @CommunalLog logBuffer: LogBuffer,
+) {
+ companion object {
+ private const val TAG = "CommunalWidgetHost"
+ }
+ private val logger = Logger(logBuffer, TAG)
+
+ /**
+ * Allocate an app widget id and binds the widget.
+ *
+ * @return widgetId if binding is successful; otherwise return null
+ */
+ fun allocateIdAndBindWidget(provider: ComponentName): Int? {
+ val id = appWidgetHost.allocateAppWidgetId()
+ if (bindWidget(id, provider)) {
+ logger.d("Successfully bound the widget $provider")
+ return id
+ }
+ appWidgetHost.deleteAppWidgetId(id)
+ logger.d("Failed to bind the widget $provider")
+ return null
+ }
+
+ private fun bindWidget(widgetId: Int, provider: ComponentName): Boolean =
+ appWidgetManager.bindAppWidgetIdIfAllowed(widgetId, provider)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
deleted file mode 100644
index b60dc2a..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.systemui.communal.ui.model
-
-import android.view.View
-import com.android.systemui.communal.shared.model.CommunalContentSize
-
-/**
- * Encapsulates data for a communal content that holds a view.
- *
- * This model stays in the UI layer.
- */
-data class CommunalContentUiModel(
- val id: String,
- val view: View,
- val size: CommunalContentSize = CommunalContentSize.HALF,
-)
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 de9b563..5efe6ce 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,49 +16,49 @@
package com.android.systemui.communal.ui.viewmodel
-import android.appwidget.AppWidgetHost
-import android.content.Context
+import android.content.ComponentName
import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
+import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.ui.model.CommunalContentUiModel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
@SysUISingleton
class CommunalViewModel
@Inject
constructor(
- @Application private val context: Context,
- private val appWidgetHost: AppWidgetHost,
private val communalInteractor: CommunalInteractor,
- tutorialInteractor: CommunalTutorialInteractor,
+ @Named(MediaModule.COMMUNAL_HUB) val mediaHost: MediaHost,
) {
- /** Whether communal hub should show tutorial content. */
- val showTutorialContent: Flow<Boolean> = tutorialInteractor.isTutorialAvailable
-
- /** List of widgets to be displayed in the communal hub. */
- val widgetContent: Flow<List<CommunalContentUiModel>> =
- communalInteractor.widgetContent.map { widgets ->
- widgets.map Widget@{ widget ->
- // TODO(b/306406256): As adding and removing widgets functionalities are
- // supported, cache the host views so they're not recreated each time.
- val hostView =
- appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo)
- return@Widget CommunalContentUiModel(
- // TODO(b/308148193): a more scalable solution for unique ids.
- id = "widget_${widget.appWidgetId}",
- view = hostView,
- )
- }
- }
-
val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
fun onSceneChanged(scene: CommunalSceneKey) {
communalInteractor.onSceneChanged(scene)
}
+
+ /** A list of all the communal content to be displayed in the communal hub. */
+ val communalContent: Flow<List<CommunalContentModel>> = communalInteractor.communalContent
+
+ /** 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
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
index b98794e..9bb23d8 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
@@ -71,6 +71,18 @@
private final DreamOverlayStateController mDreamOverlayStateController;
private final SmartSpaceComplication mComplication;
private final FeatureFlags mFeatureFlags;
+ private final DreamOverlayStateController.Callback mStateControllerCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ if (mDreamOverlayStateController.isOverlayActive()) {
+ mSmartSpaceController.addListener(mSmartspaceListener);
+ } else {
+ mSmartSpaceController.removeListener(mSmartspaceListener);
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+ }
+ };
private final BcSmartspaceDataPlugin.SmartspaceTargetListener mSmartspaceListener =
new BcSmartspaceDataPlugin.SmartspaceTargetListener() {
@@ -103,17 +115,7 @@
return;
}
- mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
- @Override
- public void onStateChanged() {
- if (mDreamOverlayStateController.isOverlayActive()) {
- mSmartSpaceController.addListener(mSmartspaceListener);
- } else {
- mSmartSpaceController.removeListener(mSmartspaceListener);
- mDreamOverlayStateController.removeComplication(mComplication);
- }
- }
- });
+ mDreamOverlayStateController.addCallback(mStateControllerCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 58dcf069..b34b459 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -37,6 +37,7 @@
import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule;
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractorModule;
import com.android.systemui.bouncer.ui.BouncerViewModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
@@ -129,6 +130,7 @@
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
import com.android.systemui.util.kotlin.CoroutinesModule;
+import com.android.systemui.util.reference.ReferenceModule;
import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
@@ -166,6 +168,7 @@
AuthenticationModule.class,
BiometricsModule.class,
BiometricsDomainLayerModule.class,
+ BouncerInteractorModule.class,
BouncerViewModule.class,
ClipboardOverlayModule.class,
ClockRegistryModule.class,
@@ -199,6 +202,7 @@
PrivacyModule.class,
QRCodeScannerModule.class,
QSFragmentStartableModule.class,
+ ReferenceModule.class,
RetailModeModule.class,
ScreenshotModule.class,
SensorModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index cf86885..20a9e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -20,15 +20,18 @@
import android.companion.virtual.flags.Flags
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/** Provides information about an external connected display. */
@@ -81,6 +84,7 @@
private val virtualDeviceManager: VirtualDeviceManager,
keyguardRepository: KeyguardRepository,
displayRepository: DisplayRepository,
+ @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
) : ConnectedDisplayInteractor {
override val connectedDisplayState: Flow<State> =
@@ -101,6 +105,7 @@
State.CONNECTED
}
}
+ .flowOn(backgroundCoroutineDispatcher)
.distinctUntilChanged()
override val connectedDisplayAddition: Flow<Unit> =
@@ -108,6 +113,7 @@
.filter {
it != null && (isExternalDisplay(it) || isVirtualDeviceOwnedMirrorDisplay(it))
}
+ .flowOn(backgroundCoroutineDispatcher)
.map {} // map to Unit
// Provides the pending display only if the lockscreen is unlocked
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
index 87b0f01..d500d1c2 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -20,7 +20,6 @@
import android.view.View
import android.widget.TextView
import androidx.core.view.updatePadding
-import com.android.systemui.biometrics.Utils
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIBottomSheetDialog
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -36,6 +35,7 @@
context: Context,
private val onStartMirroringClickListener: View.OnClickListener,
private val onCancelMirroring: View.OnClickListener,
+ private val navbarBottomInsetsProvider: () -> Int,
configurationController: ConfigurationController? = null,
theme: Int = R.style.Theme_SystemUI_Dialog,
) : SystemUIBottomSheetDialog(context, configurationController, theme) {
@@ -67,12 +67,12 @@
private fun setupInsets() {
// This avoids overlap between dialog content and navigation bars.
requireViewById<View>(R.id.cd_bottom_sheet).apply {
- val navbarInsets = Utils.getNavbarInsets(context)
+ val navbarInsets = navbarBottomInsetsProvider()
val defaultDialogBottomInset =
context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)
// we only care about the bottom inset as in all other configuration where navigations
// are in other display sides there is no overlap with the dialog.
- updatePadding(bottom = max(navbarInsets.bottom, defaultDialogBottomInset))
+ updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index 91f535d..19b4d22 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -17,6 +17,7 @@
import android.app.Dialog
import android.content.Context
+import com.android.systemui.biometrics.Utils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -74,7 +75,8 @@
scope.launch(bgDispatcher) { pendingDisplay.ignore() }
hideDialog()
},
- configurationController
+ navbarBottomInsetsProvider = { Utils.getNavbarInsets(context).bottom },
+ configurationController,
)
.apply { show() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index c9748f9..0e333f2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -31,11 +31,15 @@
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
+import com.android.systemui.util.reference.WeakReferenceFactory;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -68,7 +72,10 @@
/**
* Callback for dream overlay events.
+ * NOTE: Caller should maintain a strong reference to this themselves so the callback does
+ * not get garbage collected.
*/
+ @WeaklyReferencedCallback
public interface Callback {
/**
* Called when the composition of complications changes.
@@ -97,7 +104,7 @@
private final Executor mExecutor;
private final boolean mOverlayEnabled;
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
@Complication.ComplicationType
private int mAvailableComplicationTypes = Complication.COMPLICATION_TYPE_NONE;
@@ -107,6 +114,7 @@
private final Collection<Complication> mComplications = new HashSet();
private final FeatureFlags mFeatureFlags;
+ private final WeakReferenceFactory mWeakReferenceFactory;
private final int mSupportedTypes;
@@ -117,11 +125,13 @@
public DreamOverlayStateController(@Main Executor executor,
@Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled,
FeatureFlags featureFlags,
- @DreamLog LogBuffer logBuffer) {
+ @DreamLog LogBuffer logBuffer,
+ WeakReferenceFactory weakReferenceFactory) {
mExecutor = executor;
mOverlayEnabled = overlayEnabled;
mLogger = new DreamLogger(logBuffer, TAG);
mFeatureFlags = featureFlags;
+ mWeakReferenceFactory = weakReferenceFactory;
if (mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)) {
mSupportedTypes = Complication.COMPLICATION_TYPE_NONE
| Complication.COMPLICATION_TYPE_HOME_CONTROLS;
@@ -143,7 +153,7 @@
mExecutor.execute(() -> {
if (mComplications.add(complication)) {
mLogger.logAddComplication(complication.toString());
- mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
+ notifyCallbacksLocked(Callback::onComplicationsChanged);
}
});
}
@@ -160,7 +170,7 @@
mExecutor.execute(() -> {
if (mComplications.remove(complication)) {
mLogger.logRemoveComplication(complication.toString());
- mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
+ notifyCallbacksLocked(Callback::onComplicationsChanged);
}
});
}
@@ -199,22 +209,33 @@
}
private void notifyCallbacks(Consumer<Callback> callbackConsumer) {
- mExecutor.execute(() -> {
- for (Callback callback : mCallbacks) {
+ mExecutor.execute(() -> notifyCallbacksLocked(callbackConsumer));
+ }
+
+ private void notifyCallbacksLocked(Consumer<Callback> callbackConsumer) {
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback callback = iterator.next().get();
+ // Remove any callbacks which have been GC'd
+ if (callback == null) {
+ iterator.remove();
+ } else {
callbackConsumer.accept(callback);
}
- });
+ }
}
@Override
public void addCallback(@NonNull Callback callback) {
mExecutor.execute(() -> {
Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- if (mCallbacks.contains(callback)) {
+ final boolean containsCallback = mCallbacks.stream()
+ .anyMatch(reference -> reference.get() == callback);
+ if (containsCallback) {
return;
}
- mCallbacks.add(callback);
+ mCallbacks.add(mWeakReferenceFactory.create(callback));
if (mComplications.isEmpty()) {
return;
@@ -228,7 +249,13 @@
public void removeCallback(@NonNull Callback callback) {
mExecutor.execute(() -> {
Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- mCallbacks.remove(callback);
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
});
}
@@ -318,7 +345,7 @@
if (isLowLightActive() && !active) {
// Notify that we're exiting low light only on the transition from active to not active.
- mCallbacks.forEach(Callback::onExitLowLight);
+ notifyCallbacks(Callback::onExitLowLight);
}
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE);
}
@@ -375,7 +402,7 @@
mExecutor.execute(() -> {
mLogger.logAvailableComplicationTypes(types);
mAvailableComplicationTypes = types;
- mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
+ notifyCallbacksLocked(Callback::onAvailableComplicationTypesChanged);
});
}
@@ -393,7 +420,7 @@
mExecutor.execute(() -> {
mLogger.logShouldShowComplications(shouldShowComplications);
mShouldShowComplications = shouldShowComplications;
- mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
+ notifyCallbacksLocked(Callback::onAvailableComplicationTypesChanged);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 20925fe..dd971b9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -68,9 +68,6 @@
"notification_drag_to_contents"
)
- // TODO(b/254512538): Tracking Bug
- val INSTANT_VOICE_REPLY = unreleasedFlag("instant_voice_reply")
-
/**
* This flag controls whether we register a listener for StatsD notification memory reports.
* For statsd to actually call the listener however, a server-side toggle needs to be
@@ -247,15 +244,6 @@
/** Keyguard Migration */
- /**
- * Migrate the bottom area to the new keyguard root view. Because there is no such thing as a
- * "bottom area" after this, this also breaks it up into many smaller, modular pieces.
- */
- // TODO(b/290652751): Tracking bug.
- @JvmField
- val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
- unreleasedFlag("migrate_split_keyguard_bottom_area")
-
// TODO(b/297037052): Tracking bug.
@JvmField
val REMOVE_NPVC_BOTTOM_AREA_USAGE = unreleasedFlag("remove_npvc_bottom_area_usage")
@@ -267,10 +255,6 @@
// TODO(b/287268101): Tracking bug.
@JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock")
- /** Migrate the lock icon view to the new keyguard root view. */
- // TODO(b/286552209): Tracking bug.
- @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag("migrate_lock_icon")
-
// TODO(b/288276738): Tracking bug.
@JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag("widget_on_keyguard")
@@ -394,7 +378,7 @@
@JvmField val SIGNAL_CALLBACK_DEPRECATION = releasedFlag("signal_callback_deprecation")
// TODO(b/301610137): Tracking bug
- @JvmField val NEW_NETWORK_SLICE_UI = unreleasedFlag("new_network_slice_ui", teamfood = true)
+ @JvmField val NEW_NETWORK_SLICE_UI = releasedFlag("new_network_slice_ui")
// TODO(b/308138154): Tracking bug
val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS =
@@ -497,12 +481,6 @@
namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
)
- // TODO(b/254512674): Tracking Bug
- @Keep
- @JvmField
- val HIDE_NAVBAR_WINDOW =
- sysPropBooleanFlag("persist.wm.debug.hide_navbar_window", default = false)
-
@Keep
@JvmField
val WM_CAPTION_ON_SHELL =
@@ -792,10 +770,6 @@
@JvmField
val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
- /** Enable haptic slider component in the brightness slider */
- @JvmField
- val HAPTIC_BRIGHTNESS_SLIDER = unreleasedFlag("haptic_brightness_slider", teamfood = true)
-
// TODO(b/287205379): Tracking bug
@JvmField
val QS_CONTAINER_GRAPH_OPTIMIZER = releasedFlag( "qs_container_graph_optimizer")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 61c8e1bb..1037b0e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -27,6 +27,7 @@
import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
@@ -113,7 +114,7 @@
fun bindIndicationArea() {
indicationAreaHandle?.dispose()
- if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let {
keyguardRootView.removeView(it)
}
@@ -125,7 +126,6 @@
keyguardIndicationAreaViewModel,
keyguardRootViewModel,
indicationController,
- featureFlags,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 654f2d1..f5f5571 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -66,7 +66,7 @@
/**
* Acts as source of truth for biometric authentication related settings like enrollments, device
- * policy, etc.
+ * policy specifically for device entry usage.
*
* Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
* upstream changes.
@@ -74,7 +74,8 @@
interface BiometricSettingsRepository {
/**
* If the current user can enter the device using fingerprint. This is true if user has enrolled
- * fingerprints and fingerprint auth is not disabled through settings/device policy
+ * fingerprints and fingerprint auth is not disabled for device entry through settings and
+ * device policy
*/
val isFingerprintEnrolledAndEnabled: StateFlow<Boolean>
@@ -247,9 +248,11 @@
}
}
- private val isFaceEnabledByBiometricsManagerForCurrentUser: Flow<Boolean> =
+ private val areBiometricsEnabledForCurrentUser: Flow<Boolean> =
userRepository.selectedUserInfo.flatMapLatest { userInfo ->
- isFaceEnabledByBiometricsManager.map { biometricsEnabledForUser[userInfo.id] ?: false }
+ areBiometricsEnabledForDeviceEntryFromUserSetting.map {
+ biometricsEnabledForUser[userInfo.id] ?: false
+ }
}
private val isFaceEnabledByDevicePolicy: Flow<Boolean> =
@@ -263,13 +266,13 @@
.distinctUntilChanged()
private val isFaceAuthenticationEnabled: Flow<Boolean> =
- combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) {
+ combine(areBiometricsEnabledForCurrentUser, isFaceEnabledByDevicePolicy) {
biometricsManagerSetting,
devicePolicySetting ->
biometricsManagerSetting && devicePolicySetting
}
- private val isFaceEnabledByBiometricsManager: Flow<Pair<Int, Boolean>> =
+ private val areBiometricsEnabledForDeviceEntryFromUserSetting: Flow<Pair<Int, Boolean>> =
conflatedCallbackFlow {
val callback =
object : IBiometricEnabledOnKeyguardCallback.Stub() {
@@ -340,6 +343,7 @@
override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> =
isFingerprintEnrolled
+ .and(areBiometricsEnabledForCurrentUser)
.and(isFingerprintEnabledByDevicePolicy)
.stateIn(scope, SharingStarted.Eagerly, false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt
new file mode 100644
index 0000000..d23899b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.keyguard.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.SmartspaceState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/**
+ * State related to System UI's handling of the in-window Launcher unlock animations. This includes
+ * the staggered icon entry animation that plays during unlock, as well as the smartspace shared
+ * element animation, if supported.
+ *
+ * While the animations themselves occur fully in the Launcher window, System UI is responsible for
+ * preparing/starting the animations, as well as synchronizing the smartspace state so that the two
+ * smartspaces appear visually identical for the shared element animation.
+ */
+@SysUISingleton
+class InWindowLauncherUnlockAnimationRepository @Inject constructor() {
+
+ /**
+ * Whether we have called [ILauncherUnlockAnimationController.playUnlockAnimation] during this
+ * unlock sequence. This value is set back to false once
+ * [InWindowLauncherUnlockAnimationInteractor.shouldStartInWindowAnimation] reverts to false,
+ * which happens when we're no longer in transition to GONE or if the remote animation ends or
+ * is cancelled.
+ */
+ val startedUnlockAnimation = MutableStateFlow(false)
+
+ /**
+ * The unlock amount we've explicitly passed to
+ * [ILauncherUnlockAnimationController.setUnlockAmount]. This is used whenever System UI is
+ * directly controlling the amount of the unlock animation, such as during a manual swipe to
+ * unlock gesture.
+ *
+ * This value is *not* updated if we called
+ * [ILauncherUnlockAnimationController.playUnlockAnimation] to ask Launcher to animate all the
+ * way unlocked, since that animator is running in the Launcher window.
+ */
+ val manualUnlockAmount: MutableStateFlow<Float?> = MutableStateFlow(null)
+
+ /**
+ * The class name of the Launcher activity that provided us with a
+ * [ILauncherUnlockAnimationController], if applicable. We can use this to check if that
+ * launcher is underneath the lockscreen before playing in-window animations.
+ *
+ * If null, we have not been provided with a launcher unlock animation controller.
+ */
+ val launcherActivityClass: MutableStateFlow<String?> = MutableStateFlow(null)
+
+ /**
+ * Information about the Launcher's smartspace, which is passed to us via
+ * [ILauncherUnlockAnimationController].
+ */
+ val launcherSmartspaceState: MutableStateFlow<SmartspaceState?> = MutableStateFlow(null)
+
+ fun setStartedUnlockAnimation(started: Boolean) {
+ startedUnlockAnimation.value = started
+ }
+
+ fun setManualUnlockAmount(amount: Float?) {
+ manualUnlockAmount.value = amount
+ }
+
+ fun setLauncherActivityClass(className: String) {
+ launcherActivityClass.value = className
+ }
+
+ fun setLauncherSmartspaceState(state: SmartspaceState?) {
+ launcherSmartspaceState.value = state
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt
index 014b7fa..6121b633 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt
@@ -31,8 +31,14 @@
/** Whether we're running animations on the surface. */
val isAnimatingSurface: Flow<Boolean>
+ /** Whether we have a RemoteAnimationTarget to run animations on the surface. */
+ val isSurfaceRemoteAnimationTargetAvailable: Flow<Boolean>
+
/** Set whether we're running animations on the surface. */
fun setAnimatingSurface(animating: Boolean)
+
+ /** Set whether we have a RemoteAnimationTarget with which to run animations on the surface. */
+ fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean)
}
@SysUISingleton
@@ -40,7 +46,15 @@
private val _isAnimatingSurface = MutableStateFlow(false)
override val isAnimatingSurface = _isAnimatingSurface.asStateFlow()
+ private val _isSurfaceRemoteAnimationTargetAvailable = MutableStateFlow(false)
+ override val isSurfaceRemoteAnimationTargetAvailable =
+ _isSurfaceRemoteAnimationTargetAvailable.asStateFlow()
+
override fun setAnimatingSurface(animating: Boolean) {
_isAnimatingSurface.value = animating
}
+
+ override fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) {
+ _isSurfaceRemoteAnimationTargetAvailable.value = available
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index c4962a1..ea40ba0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -35,6 +35,7 @@
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -56,6 +57,7 @@
private val flags: FeatureFlags,
private val shadeRepository: ShadeRepository,
private val powerInteractor: PowerInteractor,
+ inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
) :
TransitionInteractor(
fromState = KeyguardState.LOCKSCREEN,
@@ -104,12 +106,21 @@
val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
combine(
transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN)
- ) { startedStep, fromLockscreenStep ->
+ transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN),
+ inWindowLauncherUnlockAnimationInteractor
+ .get()
+ .transitioningToGoneWithInWindowAnimation,
+ ) { startedStep, fromLockscreenStep, transitioningToGoneWithInWindowAnimation ->
if (startedStep.to != KeyguardState.GONE) {
// Only LOCKSCREEN -> GONE has specific surface params (for the unlock
// animation).
return@combine null
+ } else if (transitioningToGoneWithInWindowAnimation) {
+ // If we're prepared for the in-window unlock, we're going to play an animation
+ // in the window. Make it fully visible.
+ KeyguardSurfaceBehindModel(
+ alpha = 1f,
+ )
} else if (fromLockscreenStep.value > 0.5f) {
// Start the animation once we're 50% transitioned to GONE.
KeyguardSurfaceBehindModel(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt
new file mode 100644
index 0000000..e7d74a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt
@@ -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 com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
+import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.smartspace.SmartspaceState
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class InWindowLauncherUnlockAnimationInteractor
+@Inject
+constructor(
+ private val repository: InWindowLauncherUnlockAnimationRepository,
+ @Application scope: CoroutineScope,
+ transitionInteractor: KeyguardTransitionInteractor,
+ surfaceBehindRepository: dagger.Lazy<KeyguardSurfaceBehindRepository>,
+ private val activityManager: ActivityManagerWrapper,
+) {
+ val startedUnlockAnimation = repository.startedUnlockAnimation.asStateFlow()
+
+ /**
+ * Whether we've STARTED but not FINISHED a transition to GONE, and the preconditions are met to
+ * play the in-window unlock animation.
+ */
+ val transitioningToGoneWithInWindowAnimation: StateFlow<Boolean> =
+ transitionInteractor
+ .isInTransitionToState(KeyguardState.GONE)
+ .sample(repository.launcherActivityClass, ::Pair)
+ .map { (isTransitioningToGone, launcherActivityClass) ->
+ isTransitioningToGone && isActivityClassUnderneath(launcherActivityClass)
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ /**
+ * Whether we should start the in-window unlock animation.
+ *
+ * This emits true once the Launcher surface becomes available while we're
+ * [transitioningToGoneWithInWindowAnimation].
+ */
+ val shouldStartInWindowAnimation: StateFlow<Boolean> =
+ combine(
+ transitioningToGoneWithInWindowAnimation,
+ surfaceBehindRepository.get().isSurfaceRemoteAnimationTargetAvailable,
+ ) { transitioningWithInWindowAnimation, isSurfaceAvailable ->
+ transitioningWithInWindowAnimation && isSurfaceAvailable
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
+ /** Sets whether we've started */
+ fun setStartedUnlockAnimation(started: Boolean) {
+ repository.setStartedUnlockAnimation(started)
+ }
+
+ fun setManualUnlockAmount(amount: Float) {
+ repository.setManualUnlockAmount(amount)
+ }
+
+ fun setLauncherActivityClass(className: String) {
+ repository.setLauncherActivityClass(className)
+ }
+
+ fun setLauncherSmartspaceState(state: SmartspaceState?) {
+ repository.setLauncherSmartspaceState(state)
+ }
+
+ /**
+ * Whether an activity with the given [activityClass] name is currently underneath the
+ * lockscreen (it's at the top of the activity task stack).
+ */
+ private fun isActivityClassUnderneath(activityClass: String?): Boolean {
+ return activityClass?.let {
+ activityManager.runningTask?.topActivity?.className?.equals(it)
+ }
+ ?: false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index bf04f8f..efbe261 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -20,13 +20,13 @@
import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
+import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
@SysUISingleton
class KeyguardSurfaceBehindInteractor
@@ -40,19 +40,18 @@
@OptIn(ExperimentalCoroutinesApi::class)
val viewParams: Flow<KeyguardSurfaceBehindModel> =
- transitionInteractor.isInTransitionToAnyState
- .flatMapLatest { isInTransition ->
- if (!isInTransition) {
- defaultParams
- } else {
- combine(
- transitionSpecificViewParams,
- defaultParams,
- ) { transitionParams, defaultParams ->
- transitionParams ?: defaultParams
- }
+ transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition ->
+ if (!isInTransition) {
+ defaultParams
+ } else {
+ combine(
+ transitionSpecificViewParams,
+ defaultParams,
+ ) { transitionParams, defaultParams ->
+ transitionParams ?: defaultParams
}
}
+ }
val isAnimatingSurface = repository.isAnimatingSurface
@@ -86,4 +85,8 @@
fun setAnimatingSurface(animating: Boolean) {
repository.setAnimatingSurface(animating)
}
+
+ fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) {
+ repository.setSurfaceRemoteAnimationTargetAvailable(available)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt
new file mode 100644
index 0000000..56a6e9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherAnimationViewBinder.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.keyguard.ui.binder
+
+import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Binds the [InWindowLauncherUnlockAnimationManager] "view", which manages the lifecycle and state
+ * of the in-window Launcher animation.
+ */
+object InWindowLauncherAnimationViewBinder {
+
+ @JvmStatic
+ fun bind(
+ viewModel: InWindowLauncherAnimationViewModel,
+ inWindowLauncherUnlockAnimationManager: InWindowLauncherUnlockAnimationManager,
+ scope: CoroutineScope
+ ) {
+ scope.launch {
+ viewModel.shouldPrepareForInWindowAnimation.collect { shouldPrepare ->
+ if (shouldPrepare) {
+ inWindowLauncherUnlockAnimationManager.prepareForUnlock()
+ } else {
+ // If we no longer meet the conditions to prepare for unlock, we'll need to
+ // manually set Launcher unlocked if we didn't start the unlock animation, or it
+ // will remain "prepared" (blank) forever.
+ inWindowLauncherUnlockAnimationManager.ensureUnlockedOrAnimatingUnlocked()
+ }
+ }
+ }
+
+ scope.launch {
+ viewModel.shouldStartInWindowAnimation.collect { shouldStart ->
+ if (shouldStart) {
+ inWindowLauncherUnlockAnimationManager.playUnlockAnimation(unlocked = true)
+ } else {
+ // Once the conditions to start the animation are no longer met, clear whether
+ // we started the animation, since we'll need to start it again if the
+ // conditions become true again.
+ inWindowLauncherUnlockAnimationManager.clearStartedUnlockAnimation()
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index f20a666..1a8f625 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -22,9 +22,8 @@
import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -54,7 +53,6 @@
viewModel: KeyguardIndicationAreaViewModel,
keyguardRootViewModel: KeyguardRootViewModel,
indicationController: KeyguardIndicationController,
- featureFlags: FeatureFlags,
): DisposableHandle {
val indicationArea: ViewGroup = view.requireViewById(R.id.keyguard_indication_area)
indicationController.setIndicationArea(indicationArea)
@@ -71,7 +69,7 @@
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
keyguardRootViewModel.alpha.collect { alpha ->
indicationArea.apply {
this.importantForAccessibility =
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 378656c..1f74bb6 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
@@ -33,6 +33,7 @@
import com.android.internal.jank.InteractionJankMonitor
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.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
@@ -111,7 +112,7 @@
}
}
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
index c8dab32..8587022 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
@@ -51,6 +51,11 @@
private val interactor: KeyguardSurfaceBehindInteractor,
) {
private var surfaceBehind: RemoteAnimationTarget? = null
+ set(value) {
+ field = value
+ interactor.setSurfaceRemoteAnimationTargetAvailable(value != null)
+ }
+
private val surfaceTransactionApplier: SyncRtSurfaceTransactionApplier
get() = SyncRtSurfaceTransactionApplier(keyguardViewController.viewRootImpl.view)
@@ -66,7 +71,7 @@
dampingRatio = 1f
}
addUpdateListener { _, _, _ -> applyToSurfaceBehind() }
- addEndListener { _, _, _, _ ->
+ addEndListener { _, _, _, _ ->
try {
updateIsAnimatingSurface()
} catch (e: NullPointerException) {
@@ -112,6 +117,7 @@
fun applyParamsToSurface(surface: RemoteAnimationTarget) {
this.surfaceBehind = surface
startOrUpdateAnimators()
+ applyToSurfaceBehind()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index b797c4b..bdd9a6bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -41,6 +41,7 @@
import androidx.core.view.isInvisible
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -156,7 +157,7 @@
private val shortcutsBindings = mutableSetOf<KeyguardQuickAffordanceViewBinder.Binding>()
init {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
keyguardRootViewModel.enablePreviewMode()
quickAffordancesCombinedViewModel.enablePreviewMode(
initiallySelectedSlotId =
@@ -199,7 +200,7 @@
setupKeyguardRootView(previewContext, rootView)
- if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
setUpBottomArea(rootView)
}
@@ -243,7 +244,7 @@
}
fun onSlotSelected(slotId: String) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId)
} else {
bottomAreaViewModel.onPreviewSlotSelected(slotId = slotId)
@@ -254,7 +255,7 @@
isDestroyed = true
lockscreenSmartspaceController.disconnect()
disposables.forEach { it.dispose() }
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
shortcutsBindings.forEach { it.destroy() }
}
}
@@ -363,7 +364,7 @@
disposables.add(
PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
setupShortcuts(keyguardRootView)
}
setUpUdfps(previewContext, rootView)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
new file mode 100644
index 0000000..eb005f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
@@ -0,0 +1,199 @@
+/*
+ *
+ * * 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.keyguard.ui.view
+
+import android.graphics.Rect
+import android.util.Log
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
+import com.android.systemui.keyguard.ui.binder.InWindowLauncherAnimationViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.SmartspaceState
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+
+private val TAG = InWindowLauncherUnlockAnimationManager::class.simpleName
+private const val UNLOCK_ANIMATION_DURATION = 633L
+private const val UNLOCK_START_DELAY = 100L
+
+/**
+ * Handles interactions between System UI and Launcher related to the in-window unlock animation.
+ *
+ * Launcher registers its unlock controller with us here, and we use that to prepare for and start
+ * the unlock animation.
+ */
+@SysUISingleton
+class InWindowLauncherUnlockAnimationManager
+@Inject
+constructor(
+ val interactor: InWindowLauncherUnlockAnimationInteractor,
+ val viewModel: InWindowLauncherAnimationViewModel,
+ @Application val scope: CoroutineScope,
+) : ISysuiUnlockAnimationController.Stub() {
+
+ /**
+ * The smartspace view on the lockscreen. This is used to perform the shared element animation
+ * between the lockscreen smartspace and the launcher one.
+ */
+ var lockscreenSmartspace: View? = null
+
+ private var launcherAnimationController: ILauncherUnlockAnimationController? = null
+
+ /**
+ * Whether we've called [ILauncherUnlockAnimationController.prepareForUnlock], and have *not*
+ * subsequently called [ILauncherUnlockAnimationController.playUnlockAnimation] or
+ * [ILauncherUnlockAnimationController.setUnlockAmount].
+ */
+ private var preparedForUnlock = false
+
+ /**
+ * Most recent value passed to [ILauncherUnlockAnimationController.setUnlockAmount] during this
+ * unlock.
+ *
+ * Null if we have not set a manual unlock amount, or once [ensureUnlockedOrAnimatingUnlocked]
+ * has been called.
+ */
+ private var manualUnlockAmount: Float? = null
+
+ /**
+ * Called from [OverviewProxyService] to provide us with the launcher unlock animation
+ * controller, which can be used to start and update the unlock animation in the launcher
+ * process.
+ */
+ override fun setLauncherUnlockController(
+ activityClass: String,
+ launcherController: ILauncherUnlockAnimationController,
+ ) {
+ interactor.setLauncherActivityClass(activityClass)
+ launcherAnimationController = launcherController
+
+ // Bind once we have a launcher controller.
+ InWindowLauncherAnimationViewBinder.bind(viewModel, this, scope)
+ }
+
+ /**
+ * Called from the launcher process when their smartspace state updates something we should know
+ * about.
+ */
+ override fun onLauncherSmartspaceStateUpdated(state: SmartspaceState?) {
+ interactor.setLauncherSmartspaceState(state)
+ }
+
+ /**
+ * Requests that the launcher prepare for unlock by becoming blank and optionally positioning
+ * its smartspace at the same position as the lockscreen smartspace.
+ *
+ * This state is dangerous - the launcher will remain blank until we ask it to animate unlocked,
+ * either via [playUnlockAnimation] or [setUnlockAmount]. If you don't want to get funny but bad
+ * bugs titled "tiny launcher" or "Expected: launcher icons; Actual: no icons ever", be very
+ * careful here.
+ */
+ fun prepareForUnlock() {
+ launcherAnimationController?.let { launcher ->
+ if (!preparedForUnlock) {
+ preparedForUnlock = true
+ manualUnlockAmount = null
+
+ launcher.prepareForUnlock(
+ false,
+ Rect(),
+ 0
+ ) // TODO(b/293894758): Add smartspace animation support.
+ }
+ }
+ }
+
+ /** Ensures that the launcher is either fully visible, or animating to be fully visible. */
+ fun ensureUnlockedOrAnimatingUnlocked() {
+ val preparedButDidNotStartAnimation =
+ preparedForUnlock && !interactor.startedUnlockAnimation.value
+ val manualUnlockSetButNotFullyVisible =
+ manualUnlockAmount != null && manualUnlockAmount != 1f
+
+ if (preparedButDidNotStartAnimation) {
+ Log.e(
+ TAG,
+ "Called prepareForUnlock(), but not playUnlockAnimation(). " +
+ "Failing-safe by calling setUnlockAmount(1f)"
+ )
+ setUnlockAmount(1f, forceIfAnimating = true)
+ } else if (manualUnlockSetButNotFullyVisible) {
+ Log.e(
+ TAG,
+ "Unlock has ended, but manual unlock amount != 1f. " +
+ "Failing-safe by calling setUnlockAmount(1f)"
+ )
+ setUnlockAmount(1f, forceIfAnimating = true)
+ }
+
+ manualUnlockAmount = null // Un-set the manual unlock amount as we're now visible.
+ }
+
+ /**
+ * Asks launcher to play the in-window unlock animation with the specified parameters.
+ *
+ * Once this is called, we're no longer [preparedForUnlock] as unlock is underway.
+ */
+ fun playUnlockAnimation(
+ unlocked: Boolean,
+ duration: Long = UNLOCK_ANIMATION_DURATION,
+ startDelay: Long = UNLOCK_START_DELAY,
+ ) {
+ if (preparedForUnlock) {
+ launcherAnimationController?.let { launcher ->
+ launcher.playUnlockAnimation(unlocked, duration, startDelay)
+ interactor.setStartedUnlockAnimation(true)
+ }
+ } else {
+ Log.e(TAG, "Attempted to call playUnlockAnimation() before prepareToUnlock().")
+ }
+
+ preparedForUnlock = false
+ }
+
+ /**
+ * Clears the played unlock animation flag. Since we don't have access to an onAnimationEnd
+ * event for the launcher animation (since it's in a different process), this is called whenever
+ * the transition to GONE ends or the surface becomes unavailable. In both cases, we'd need to
+ * play the animation next time we unlock.
+ */
+ fun clearStartedUnlockAnimation() {
+ interactor.setStartedUnlockAnimation(false)
+ }
+
+ /**
+ * Manually sets the unlock amount on launcher. This is used to explicitly set us to fully
+ * unlocked, or to manually control the animation (such as during a swipe to unlock).
+ *
+ * Once this is called, we're no longer [preparedForUnlock] since the Launcher icons are not
+ * configured to be invisible for the start of the unlock animation.
+ */
+ fun setUnlockAmount(amount: Float, forceIfAnimating: Boolean) {
+ preparedForUnlock = false
+
+ launcherAnimationController?.let {
+ manualUnlockAmount = amount
+ it.setUnlockAmount(amount, forceIfAnimating)
+ }
+ }
+}
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 28e6a95..eb01d4f6 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
@@ -25,10 +25,9 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
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.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -41,7 +40,6 @@
@Inject
constructor(
@Main private val resources: Resources,
- private val featureFlags: FeatureFlags,
private val keyguardQuickAffordancesCombinedViewModel:
KeyguardQuickAffordancesCombinedViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
@@ -50,14 +48,14 @@
private val vibratorHelper: VibratorHelper,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
addLeftShortcut(constraintLayout)
addRightShortcut(constraintLayout)
}
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
leftShortcutHandle =
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index 9371d4e..20cb9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -29,9 +29,8 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
@@ -42,14 +41,13 @@
@Inject
constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val featureFlags: FeatureFlags,
private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
) : KeyguardSection() {
private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
val view =
LayoutInflater.from(constraintLayout.context)
.inflate(R.layout.ambient_indication, constraintLayout, false)
@@ -59,7 +57,7 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
ambientIndicationAreaHandle =
KeyguardAmbientIndicationAreaViewBinder.bind(
constraintLayout,
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 755549b..ace970a 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
@@ -29,6 +29,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -60,7 +61,7 @@
private val deviceEntryIconViewId = R.id.device_entry_icon_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON) &&
+ if (!keyguardBottomAreaRefactor() &&
!featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
) {
return
@@ -74,7 +75,7 @@
if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
} else {
- // Flags.MIGRATE_LOCK_ICON
+ // keyguardBottomAreaRefactor()
LockIconView(context, null).apply { id = R.id.lock_icon_view }
}
constraintLayout.addView(view)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index 623eac0..8aef7c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -21,9 +21,8 @@
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
@@ -40,27 +39,25 @@
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
private val indicationController: KeyguardIndicationController,
- private val featureFlags: FeatureFlags,
) : KeyguardSection() {
private val indicationAreaViewId = R.id.keyguard_indication_area
private var indicationAreaHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
val view = KeyguardIndicationArea(context, null)
constraintLayout.addView(view)
}
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
indicationAreaHandle =
KeyguardIndicationAreaBinder.bind(
constraintLayout,
keyguardIndicationAreaViewModel,
keyguardRootViewModel,
indicationController,
- featureFlags,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index 6fd13e0..9a33f08 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -28,6 +28,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import androidx.core.view.isVisible
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.res.R
import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.dagger.qualifiers.Main
@@ -45,7 +46,6 @@
@Inject
constructor(
@Main private val resources: Resources,
- private val featureFlags: FeatureFlags,
private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
private val vibratorHelper: VibratorHelper,
private val activityStarter: ActivityStarter,
@@ -53,7 +53,7 @@
private var settingsPopupMenuHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
return
}
val view =
@@ -68,7 +68,7 @@
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
settingsPopupMenuHandle =
KeyguardSettingsViewBinder.bind(
constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index a679120..0f6a966 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -24,6 +24,7 @@
import androidx.constraintlayout.widget.ConstraintSet.LEFT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
@@ -40,7 +41,6 @@
@Inject
constructor(
@Main private val resources: Resources,
- private val featureFlags: FeatureFlags,
private val keyguardQuickAffordancesCombinedViewModel:
KeyguardQuickAffordancesCombinedViewModel,
private val keyguardRootViewModel: KeyguardRootViewModel,
@@ -49,14 +49,14 @@
private val vibratorHelper: VibratorHelper,
) : BaseShortcutSection() {
override fun addViews(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
addLeftShortcut(constraintLayout)
addRightShortcut(constraintLayout)
}
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
leftShortcutHandle =
KeyguardQuickAffordanceViewBinder.bind(
constraintLayout.requireViewById(R.id.start_button),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/InWindowLauncherAnimationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/InWindowLauncherAnimationViewModel.kt
new file mode 100644
index 0000000..2807558
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/InWindowLauncherAnimationViewModel.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
+import javax.inject.Inject
+
+@SysUISingleton
+class InWindowLauncherAnimationViewModel
+@Inject
+constructor(interactor: InWindowLauncherUnlockAnimationInteractor) {
+
+ /**
+ * Whether we should call [ILauncherUnlockAnimationController.prepareForUnlock] to set up the
+ * Launcher icons for the in-window unlock.
+ *
+ * We'll do this as soon as we're transitioning to GONE when the necessary preconditions are
+ * met.
+ */
+ val shouldPrepareForInWindowAnimation = interactor.transitioningToGoneWithInWindowAnimation
+
+ /**
+ * Whether we should call [ILauncherUnlockAnimationController.playUnlockAnimation] to start the
+ * in-window unlock animation.
+ */
+ val shouldStartInWindowAnimation = interactor.shouldStartInWindowAnimation
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index 980cc1b..2327c02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -16,9 +16,8 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import javax.inject.Inject
@@ -36,7 +35,6 @@
keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
private val burnInHelperWrapper: BurnInHelperWrapper,
private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
- private val featureFlags: FeatureFlags,
) {
/** Notifies when a new configuration is set */
@@ -47,7 +45,7 @@
/** An observable for whether the indication area should be padded. */
val isIndicationAreaPadded: Flow<Boolean> =
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
combine(shortcutsCombinedViewModel.startButton, shortcutsCombinedViewModel.endButton) {
startButtonModel,
endButtonModel ->
@@ -64,7 +62,7 @@
}
/** An observable for the x-offset by which the indication area should be translated. */
val indicationAreaTranslationX: Flow<Float> =
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
keyguardInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
} else {
bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 83a6e58..773c292 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -23,11 +23,14 @@
import android.os.Handler
import android.os.UserHandle
import android.provider.Settings
+import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
import com.android.systemui.media.dagger.MediaModule.KEYGUARD
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
@@ -36,7 +39,11 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.println
import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.withIncreasedIndent
+import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Named
@@ -55,13 +62,18 @@
private val secureSettings: SecureSettings,
@Main private val handler: Handler,
configurationController: ConfigurationController,
- private val splitShadeStateController: SplitShadeStateController
-) {
+ private val splitShadeStateController: SplitShadeStateController,
+ dumpManager: DumpManager,
+) : Dumpable {
+ /** It's added for debugging purpose to directly see last received StatusBarState. */
+ private var lastReceivedStatusBarState = -1
init {
+ dumpManager.registerDumpable(this)
statusBarStateController.addCallback(
object : StatusBarStateController.StateListener {
override fun onStateChanged(newState: Int) {
+ lastReceivedStatusBarState = newState
refreshMediaPosition()
}
@@ -206,7 +218,17 @@
}
fun refreshMediaPosition() {
- val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD)
+ val currentState = statusBarStateController.state
+ if (lastReceivedStatusBarState != -1 && currentState != lastReceivedStatusBarState) {
+ Log.wtfStack(
+ TAG,
+ "currentState[${StatusBarState.toString(currentState)}] is " +
+ "different from the last " +
+ "received one[${StatusBarState.toString(lastReceivedStatusBarState)}]."
+ )
+ }
+
+ val keyguardOrUserSwitcher = (currentState == StatusBarState.KEYGUARD)
// mediaHost.visible required for proper animations handling
visible =
mediaHost.visible &&
@@ -263,4 +285,34 @@
visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
}
}
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.asIndenting().run {
+ println("KeyguardMediaController")
+ withIncreasedIndent {
+ println("Self", this@KeyguardMediaController)
+ println("visible", visible)
+ println("useSplitShade", useSplitShade)
+ println("allowMediaPlayerOnLockScreen", allowMediaPlayerOnLockScreen)
+ println("bypassController.bypassEnabled", bypassController.bypassEnabled)
+ println("isDozeWakeUpAnimationWaiting", isDozeWakeUpAnimationWaiting)
+ println("singlePaneContainer", singlePaneContainer)
+ println("splitShadeContainer", splitShadeContainer)
+ if (lastReceivedStatusBarState != -1) {
+ println(
+ "lastReceivedStatusBarState",
+ StatusBarState.toString(lastReceivedStatusBarState)
+ )
+ }
+ println(
+ "statusBarStateController.state",
+ StatusBarState.toString(statusBarStateController.state)
+ )
+ }
+ }
+ }
+
+ private companion object {
+ private const val TAG = "KeyguardMediaController"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index eea369f..654fffe8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.mediaprojection.permission
+import android.app.AlertDialog
import android.content.Context
import android.os.Bundle
import android.view.Gravity
@@ -28,36 +29,36 @@
import android.widget.ImageView
import android.widget.Spinner
import android.widget.TextView
+import androidx.annotation.CallSuper
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.DialogDelegate
/** Base permission dialog for screen share and recording */
-open class BaseScreenSharePermissionDialog(
- context: Context,
+abstract class BaseMediaProjectionPermissionDialogDelegate<T : AlertDialog>(
private val screenShareOptions: List<ScreenShareOption>,
private val appName: String?,
private val hostUid: Int,
private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
@DrawableRes private val dialogIconDrawable: Int? = null,
@ColorRes private val dialogIconTint: Int? = null,
-) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
+) : DialogDelegate<T>, AdapterView.OnItemSelectedListener {
private lateinit var dialogTitle: TextView
private lateinit var startButton: TextView
private lateinit var cancelButton: TextView
private lateinit var warning: TextView
private lateinit var screenShareModeSpinner: Spinner
private var hasCancelBeenLogged: Boolean = false
+ protected lateinit var dialog: AlertDialog
var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
- override fun dismiss() {
- super.dismiss()
-
- // Dismiss can be called multiple times and we only want to log once.
+ @CallSuper
+ override fun onStop(dialog: T) {
+ // onStop can be called multiple times and we only want to log once.
if (hasCancelBeenLogged) {
return
}
@@ -66,42 +67,43 @@
hasCancelBeenLogged = true
}
- public override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
- window?.setGravity(Gravity.CENTER)
- setContentView(R.layout.screen_share_dialog)
- dialogTitle = requireViewById(R.id.screen_share_dialog_title)
- warning = requireViewById(R.id.text_warning)
- startButton = requireViewById(android.R.id.button1)
- cancelButton = requireViewById(android.R.id.button2)
+ @CallSuper
+ override fun onCreate(dialog: T, savedInstanceState: Bundle?) {
+ this.dialog = dialog
+ dialog.window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+ dialog.window?.setGravity(Gravity.CENTER)
+ dialog.setContentView(R.layout.screen_share_dialog)
+ dialogTitle = dialog.requireViewById(R.id.screen_share_dialog_title)
+ warning = dialog.requireViewById(R.id.text_warning)
+ startButton = dialog.requireViewById(android.R.id.button1)
+ cancelButton = dialog.requireViewById(android.R.id.button2)
updateIcon()
initScreenShareOptions()
createOptionsView(getOptionsViewLayoutId())
}
private fun updateIcon() {
- val icon = requireViewById<ImageView>(R.id.screen_share_dialog_icon)
+ val icon = dialog.requireViewById<ImageView>(R.id.screen_share_dialog_icon)
if (dialogIconTint != null) {
- icon.setColorFilter(context.getColor(dialogIconTint))
+ icon.setColorFilter(dialog.context.getColor(dialogIconTint))
}
if (dialogIconDrawable != null) {
- icon.setImageDrawable(context.getDrawable(dialogIconDrawable))
+ icon.setImageDrawable(dialog.context.getDrawable(dialogIconDrawable))
}
}
- protected fun initScreenShareOptions() {
+ private fun initScreenShareOptions() {
selectedScreenShareOption = screenShareOptions.first()
warning.text = warningText
initScreenShareSpinner()
}
private val warningText: String
- get() = context.getString(selectedScreenShareOption.warningText, appName)
+ get() = dialog.context.getString(selectedScreenShareOption.warningText, appName)
private fun initScreenShareSpinner() {
- val adapter = OptionsAdapter(context.applicationContext, screenShareOptions)
- screenShareModeSpinner = requireViewById(R.id.screen_share_mode_spinner)
+ val adapter = OptionsAdapter(dialog.context.applicationContext, screenShareOptions)
+ screenShareModeSpinner = dialog.requireViewById(R.id.screen_share_mode_spinner)
screenShareModeSpinner.adapter = adapter
screenShareModeSpinner.onItemSelectedListener = this
}
@@ -115,7 +117,7 @@
/** Protected methods for the text updates & functionality */
protected fun setDialogTitle(@StringRes stringId: Int) {
- val title = context.getString(stringId, appName)
+ val title = dialog.context.getString(stringId, appName)
dialogTitle.text = title
}
@@ -137,7 +139,7 @@
private fun createOptionsView(@LayoutRes layoutId: Int?) {
if (layoutId == null) return
- val stub = requireViewById<View>(R.id.options_stub) as ViewStub
+ val stub = dialog.requireViewById<View>(R.id.options_stub) as ViewStub
stub.layoutResource = layoutId
stub.inflate()
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index eacfa57..039372d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -60,6 +60,7 @@
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.phone.AlertDialogWithDelegate;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
@@ -222,27 +223,30 @@
// the correct screen width when in split screen.
Context dialogContext = getApplicationContext();
if (isPartialScreenSharingEnabled()) {
- mDialog = new MediaProjectionPermissionDialog(
- dialogContext,
- getMediaProjectionConfig(),
- () -> {
- MediaProjectionPermissionDialog dialog =
- (MediaProjectionPermissionDialog) mDialog;
- ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
- grantMediaProjectionPermission(selectedOption.getMode());
- },
- () -> finish(RECORD_CANCEL, /* projection= */ null),
- appName,
- mUid,
- mMediaProjectionMetricsLogger);
+ MediaProjectionPermissionDialogDelegate delegate =
+ new MediaProjectionPermissionDialogDelegate(
+ dialogContext,
+ getMediaProjectionConfig(),
+ dialog -> {
+ ScreenShareOption selectedOption =
+ dialog.getSelectedScreenShareOption();
+ grantMediaProjectionPermission(selectedOption.getMode());
+ },
+ () -> finish(RECORD_CANCEL, /* projection= */ null),
+ appName,
+ mUid,
+ mMediaProjectionMetricsLogger);
+ mDialog =
+ new AlertDialogWithDelegate(
+ dialogContext, R.style.Theme_SystemUI_Dialog, delegate);
} else {
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
- R.style.Theme_SystemUI_Dialog)
- .setTitle(dialogTitle)
- .setIcon(R.drawable.ic_media_projection_permission)
- .setMessage(dialogText)
- .setPositiveButton(R.string.media_projection_action_text, this)
- .setNeutralButton(android.R.string.cancel, this);
+ AlertDialog.Builder dialogBuilder =
+ new AlertDialog.Builder(dialogContext, R.style.Theme_SystemUI_Dialog)
+ .setTitle(dialogTitle)
+ .setIcon(R.drawable.ic_media_projection_permission)
+ .setMessage(dialogText)
+ .setPositiveButton(R.string.media_projection_action_text, this)
+ .setNeutralButton(android.R.string.cancel, this);
mDialog = dialogBuilder.create();
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index cff22b0..8453af1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -15,31 +15,32 @@
*/
package com.android.systemui.mediaprojection.permission
+import android.app.AlertDialog
import android.content.Context
import android.media.projection.MediaProjectionConfig
import android.os.Bundle
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.res.R
+import java.util.function.Consumer
/** Dialog to select screen recording options */
-class MediaProjectionPermissionDialog(
+class MediaProjectionPermissionDialogDelegate(
context: Context,
mediaProjectionConfig: MediaProjectionConfig?,
- private val onStartRecordingClicked: Runnable,
+ private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
private val onCancelClicked: Runnable,
private val appName: String?,
hostUid: Int,
mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
- BaseScreenSharePermissionDialog(
- context,
+ BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
createOptionList(context, appName, mediaProjectionConfig),
appName,
hostUid,
mediaProjectionMetricsLogger
) {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
+ override fun onCreate(dialog: AlertDialog, savedInstanceState: Bundle?) {
+ super.onCreate(dialog, savedInstanceState)
// TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
if (appName == null) {
setDialogTitle(R.string.media_projection_entry_cast_permission_dialog_title)
@@ -51,12 +52,12 @@
setStartButtonOnClickListener {
// Note that it is important to run this callback before dismissing, so that the
// callback can disable the dialog exit animation if it wants to.
- onStartRecordingClicked.run()
- dismiss()
+ onStartRecordingClicked.accept(this)
+ dialog.dismiss()
}
setCancelButtonOnClickListener {
onCancelClicked.run()
- dismiss()
+ dialog.dismiss()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index a985236..5e3a166 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1383,7 +1383,17 @@
args.putInt(
AssistManager.INVOCATION_TYPE_KEY,
AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
- mAssistManagerLazy.get().startAssist(args);
+ // If Launcher has requested to override long press home, add a delay for the ripple.
+ // TODO(b/304146255): Remove this delay once we can exclude 3-button nav from screenshot.
+ boolean delayAssistInvocation = mAssistManagerLazy.get().shouldOverrideAssist(
+ AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+ // In practice, I think v should always be a KeyButtonView, but just being safe.
+ if (delayAssistInvocation && v instanceof KeyButtonView) {
+ ((KeyButtonView) v).setOnRippleInvisibleRunnable(
+ () -> mAssistManagerLazy.get().startAssist(args));
+ } else {
+ mAssistManagerLazy.get().startAssist(args);
+ }
mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
mView.abortCurrentGesture();
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 2928cce..79aedff 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -21,6 +21,7 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -49,8 +50,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.DisplayTracker;
@@ -83,7 +82,6 @@
private final Context mContext;
private final Handler mHandler;
private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
- private FeatureFlags mFeatureFlags;
private final SecureSettings mSecureSettings;
private final DisplayTracker mDisplayTracker;
private final DisplayManager mDisplayManager;
@@ -118,13 +116,11 @@
TaskStackChangeListeners taskStackChangeListeners,
Optional<Pip> pipOptional,
Optional<BackAnimation> backAnimation,
- FeatureFlags featureFlags,
SecureSettings secureSettings,
DisplayTracker displayTracker) {
mContext = context;
mHandler = mainHandler;
mNavigationBarComponentFactory = navigationBarComponentFactory;
- mFeatureFlags = featureFlags;
mSecureSettings = secureSettings;
mDisplayTracker = displayTracker;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
@@ -248,8 +244,8 @@
/** @return {@code true} if taskbar is enabled, false otherwise */
private boolean initializeTaskbarIfNecessary() {
// Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
- boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
- Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
+ boolean taskbarEnabled = (mIsLargeScreen || enableTaskbarNavbarUnification())
+ && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
if (taskbarEnabled) {
Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index bc4f7f25..258208d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -62,7 +62,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.ContextualButton;
@@ -73,6 +72,7 @@
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.Recents;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.rotation.FloatingRotationButton;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index dcf1a8e..6ec46f6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -58,8 +58,9 @@
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
-import com.android.systemui.res.R;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.res.R;
import com.android.systemui.shared.system.QuickStepContract;
public class KeyButtonView extends ImageView implements ButtonInterface {
@@ -439,11 +440,22 @@
if (mCode != KeyEvent.KEYCODE_UNKNOWN) {
sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
}
+ // When aborting long-press home and Launcher has requested to override it, fade out the
+ // ripple more quickly.
+ if (mCode == KeyEvent.KEYCODE_HOME && Dependency.get(AssistManager.class)
+ .shouldOverrideAssist(AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)) {
+ mRipple.speedUpNextFade();
+ }
setPressed(false);
mRipple.abortDelayedRipple();
mGestureAborted = true;
}
+ /** Run when the ripple for this button is next invisible. Only used once. */
+ public void setOnRippleInvisibleRunnable(Runnable onRippleInvisibleRunnable) {
+ mRipple.setOnInvisibleRunnable(onRippleInvisibleRunnable);
+ }
+
@Override
public void setDarkIntensity(float darkIntensity) {
mDarkIntensity = darkIntensity;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index c77f3f4..fa03dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -22,7 +22,6 @@
import android.graphics.Point
import android.os.Handler
import android.os.SystemClock
-import android.os.VibrationEffect
import android.util.Log
import android.util.MathUtils
import android.view.Gravity
@@ -37,8 +36,6 @@
import androidx.dynamicanimation.animation.DynamicAnimation
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -78,12 +75,6 @@
private const val POP_ON_INACTIVE_TO_ACTIVE_VELOCITY = 4.7f
private const val POP_ON_INACTIVE_VELOCITY = -1.5f
-internal val VIBRATE_ACTIVATED_EFFECT =
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
-
-internal val VIBRATE_DEACTIVATED_EFFECT =
- VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
-
private const val DEBUG = false
class BackPanelController
@@ -95,7 +86,6 @@
private val vibratorHelper: VibratorHelper,
private val configurationController: ConfigurationController,
private val latencyTracker: LatencyTracker,
- private val featureFlags: FeatureFlags
) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
/**
@@ -113,7 +103,6 @@
private val vibratorHelper: VibratorHelper,
private val configurationController: ConfigurationController,
private val latencyTracker: LatencyTracker,
- private val featureFlags: FeatureFlags
) {
/** Construct a [BackPanelController]. */
fun create(context: Context): BackPanelController {
@@ -126,7 +115,6 @@
vibratorHelper,
configurationController,
latencyTracker,
- featureFlags
)
backPanelController.init()
return backPanelController
@@ -992,35 +980,22 @@
val springForceOnCancelled =
params.cancelledIndicator.arrowDimens.alphaSpring?.get(0f)?.value
mView.popArrowAlpha(0f, springForceOnCancelled)
- if (!featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION))
- mainHandler.postDelayed(10L) { vibratorHelper.cancel() }
}
}
}
private fun performDeactivatedHapticFeedback() {
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
- )
- } else {
- vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
- }
+ vibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
+ )
}
private fun performActivatedHapticFeedback() {
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
- )
- } else {
- vibratorHelper.cancel()
- mainHandler.postDelayed(10L) {
- vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
- }
- }
+ vibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
+ )
}
private fun convertVelocityToAnimationFactor(
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 093d098..d9a8080 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -325,7 +325,13 @@
} else {
// TODO(b/278729185): Replace fire and forget service with a bounded service.
val intent = NoteTaskControllerUpdateService.createIntent(context)
- context.startServiceAsUser(intent, user)
+ try {
+ // If the user is stopped before 'startServiceAsUser' kicks-in, a
+ // 'SecurityException' will be thrown.
+ context.startServiceAsUser(intent, user)
+ } catch (e: SecurityException) {
+ debugLog(error = e) { "Unable to start 'NoteTaskControllerUpdateService'." }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
index 934f310..8242087 100644
--- a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -32,12 +32,12 @@
import com.android.systemui.power.shared.model.WakefulnessModel
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import javax.inject.Inject
/** Defines interface for classes that act as source of truth for power-related data. */
interface PowerRepository {
@@ -67,15 +67,22 @@
/** Wakes up the device. */
fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int)
- /** Notifies the power repository that a user touch happened. */
- fun userTouch()
+ /**
+ * Notifies the power repository that a user touch happened.
+ *
+ * @param noChangeLights If true, does not cause the keyboard backlight to turn on because of
+ * this event. This is set when the power key is pressed. We want the device to stay on while
+ * the button is down, but we're about to turn off the screen so we don't want the keyboard
+ * backlight to turn on again. Otherwise the lights flash on and then off and it looks weird.
+ */
+ fun userTouch(noChangeLights: Boolean = false)
/** Updates the wakefulness state, keeping previous values by default. */
fun updateWakefulness(
- rawState: WakefulnessState = wakefulness.value.internalWakefulnessState,
- lastWakeReason: WakeSleepReason = wakefulness.value.lastWakeReason,
- lastSleepReason: WakeSleepReason = wakefulness.value.lastSleepReason,
- powerButtonLaunchGestureTriggered: Boolean =
+ rawState: WakefulnessState = wakefulness.value.internalWakefulnessState,
+ lastWakeReason: WakeSleepReason = wakefulness.value.lastWakeReason,
+ lastSleepReason: WakeSleepReason = wakefulness.value.lastSleepReason,
+ powerButtonLaunchGestureTriggered: Boolean =
wakefulness.value.powerButtonLaunchGestureTriggered,
)
@@ -121,10 +128,10 @@
override val wakefulness = _wakefulness.asStateFlow()
override fun updateWakefulness(
- rawState: WakefulnessState,
- lastWakeReason: WakeSleepReason,
- lastSleepReason: WakeSleepReason,
- powerButtonLaunchGestureTriggered: Boolean,
+ rawState: WakefulnessState,
+ lastWakeReason: WakeSleepReason,
+ lastSleepReason: WakeSleepReason,
+ powerButtonLaunchGestureTriggered: Boolean,
) {
_wakefulness.value =
WakefulnessModel(
@@ -150,11 +157,11 @@
)
}
- override fun userTouch() {
+ override fun userTouch(noChangeLights: Boolean) {
manager.userActivity(
systemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH,
- /* flags= */ 0,
+ if (noChangeLights) PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS else 0,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index 4dc1c82..2074a14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -24,7 +24,6 @@
import com.android.systemui.log.dagger.QSTilesLogBuffers
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.StatusBarState
@@ -34,7 +33,7 @@
class QSTileLogger
@Inject
constructor(
- @QSTilesLogBuffers logBuffers: Map<TileSpec, LogBuffer>,
+ @QSTilesLogBuffers logBuffers: Map<String, LogBuffer>,
private val factory: LogBufferFactory,
private val mStatusBarStateController: StatusBarStateController,
) {
@@ -163,22 +162,15 @@
private fun TileSpec.getLogBuffer(): LogBuffer =
synchronized(logBufferCache) {
- logBufferCache.getOrPut(this) {
+ logBufferCache.getOrPut(this.spec) {
factory.create(
- "QSTileLog_${this.getLogTag()}",
+ this.getLogTag(),
BUFFER_MAX_SIZE /* maxSize */,
false /* systrace */
)
}
}
- private fun DataUpdateTrigger.toLogString(): String =
- when (this) {
- is DataUpdateTrigger.ForceUpdate -> "force"
- is DataUpdateTrigger.InitialRequest -> "init"
- is DataUpdateTrigger.UserInput<*> -> input.action.toLogString()
- }
-
private fun QSTileUserAction.toLogString(): String =
when (this) {
is QSTileUserAction.Click -> "click"
@@ -198,7 +190,7 @@
"]"
private companion object {
- const val TAG_FORMAT_PREFIX = "QSLog"
+ const val TAG_FORMAT_PREFIX = "QSLog_tile_"
const val DATA_MAX_LENGTH = 50
const val BUFFER_MAX_SIZE = 25
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 0bee48f..12a083e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -39,7 +39,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
-import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,6 +46,8 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.cancellable
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
@@ -57,6 +58,7 @@
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* Provides a hassle-free way to implement new tiles according to current System UI architecture
@@ -83,10 +85,8 @@
private val users: MutableStateFlow<UserHandle> =
MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
- private val userInputs: MutableSharedFlow<QSTileUserAction> =
- MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
- private val forceUpdates: MutableSharedFlow<Unit> =
- MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow()
+ private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow()
private val spec
get() = config.tileSpec
@@ -130,7 +130,7 @@
tileData.replayCache.isNotEmpty(),
state.replayCache.isNotEmpty()
)
- userInputs.tryEmit(userAction)
+ tileScope.launch { userInputs.emit(userAction) }
}
override fun destroy() {
@@ -151,11 +151,16 @@
emit(DataUpdateTrigger.InitialRequest)
qsTileLogger.logInitialRequest(spec)
}
+ .shareIn(tileScope, SharingStarted.WhileSubscribed())
tileDataInteractor()
.tileData(user, updateTriggers)
+ // combine makes sure updateTriggers is always listened even if
+ // tileDataInteractor#tileData doesn't flatMapLatest on it
+ .combine(updateTriggers) { data, _ -> data }
.cancellable()
.flowOn(backgroundDispatcher)
}
+ .distinctUntilChanged()
.shareIn(
tileScope,
SharingStarted.WhileSubscribed(),
@@ -171,8 +176,8 @@
*
* Subscribing to the result flow twice will result in doubling all actions, logs and analytics.
*/
- private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> {
- return userInputs
+ private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> =
+ userInputs
.filterFalseActions()
.filterByPolicy(user)
.throttle(CLICK_THROTTLE_DURATION, systemClock)
@@ -187,7 +192,6 @@
}
.onEach { userActionInteractor().handleInput(it.input) }
.flowOn(backgroundDispatcher)
- }
private fun Flow<QSTileUserAction>.filterByPolicy(user: UserHandle): Flow<QSTileUserAction> =
config.policy.let { policy ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index c4d7dfb..18a4e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -36,9 +36,9 @@
*/
sealed interface QSTileUIConfig {
- val tileIconRes: Int
+ val iconRes: Int
@DrawableRes get
- val tileLabelRes: Int
+ val labelRes: Int
@StringRes get
/**
@@ -46,16 +46,16 @@
* of [Resource]. Returns [Resources.ID_NULL] for each field.
*/
data object Empty : QSTileUIConfig {
- override val tileIconRes: Int
+ override val iconRes: Int
get() = Resources.ID_NULL
- override val tileLabelRes: Int
+ override val labelRes: Int
get() = Resources.ID_NULL
}
/** Config containing actual icon and label resources. */
data class Resource(
- @StringRes override val tileIconRes: Int,
- @StringRes override val tileLabelRes: Int,
+ @DrawableRes override val iconRes: Int,
+ @StringRes override val labelRes: Int,
) : QSTileUIConfig
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index dc5cccc..30b87cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.viewmodel
+import android.content.Context
import android.service.quicksettings.Tile
import com.android.systemui.common.shared.model.Icon
@@ -41,11 +42,19 @@
companion object {
+ fun build(
+ context: Context,
+ config: QSTileUIConfig,
+ build: Builder.() -> Unit
+ ): QSTileState =
+ build(
+ { Icon.Resource(config.iconRes, null) },
+ context.getString(config.labelRes),
+ build,
+ )
+
fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
Builder(icon, label).apply(build).build()
-
- fun build(icon: Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
- build({ icon }, label, build)
}
enum class ActivationState(val legacyState: Int) {
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 efa6da7..771d07c 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
@@ -192,7 +192,7 @@
with(qsTileViewModel.config.uiConfig) {
when (this) {
is QSTileUIConfig.Empty -> qsTileViewModel.currentState?.label ?: ""
- is QSTileUIConfig.Resource -> context.getString(tileLabelRes)
+ is QSTileUIConfig.Resource -> context.getString(labelRes)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1334660..377803f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -85,8 +85,10 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBar;
import com.android.systemui.navigationbar.NavigationBarController;
@@ -102,6 +104,7 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
@@ -109,8 +112,6 @@
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
import com.android.wm.shell.sysui.ShellInterface;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -122,6 +123,8 @@
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* Class to send information from overview to launcher with a binder.
*/
@@ -160,7 +163,7 @@
private final ScreenshotHelper mScreenshotHelper;
private final CommandQueue mCommandQueue;
private final UserTracker mUserTracker;
- private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
+ private final ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
private final Optional<UnfoldTransitionProgressForwarder> mUnfoldTransitionProgressForwarder;
private final UiEventLogger mUiEventLogger;
private final DisplayTracker mDisplayTracker;
@@ -580,6 +583,7 @@
UiEventLogger uiEventLogger,
DisplayTracker displayTracker,
KeyguardUnlockAnimationController sysuiUnlockAnimationController,
+ InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager,
AssistUtils assistUtils,
FeatureFlags featureFlags,
SceneContainerFlags sceneContainerFlags,
@@ -613,7 +617,12 @@
mUiEventLogger = uiEventLogger;
mDisplayTracker = displayTracker;
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
- mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
+
+ if (!featureFlags.isEnabled(Flags.KEYGUARD_WM_STATE_REFACTOR)) {
+ mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
+ } else {
+ mSysuiUnlockAnimationController = inWindowLauncherUnlockAnimationManager;
+ }
dumpManager.registerDumpable(getClass().getSimpleName(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 018f31b..e40d2b7 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene.shared.flag
import androidx.annotation.VisibleForTesting
+import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
@@ -57,8 +58,6 @@
@VisibleForTesting
val classicFlagTokens: List<Flag<Boolean>> =
listOf(
- Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA,
- Flags.MIGRATE_LOCK_ICON,
Flags.MIGRATE_NSSL,
Flags.MIGRATE_KEYGUARD_STATUS_VIEW,
Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW,
@@ -72,6 +71,10 @@
flagName = AConfigFlags.FLAG_SCENE_CONTAINER,
flagValue = sceneContainer(),
),
+ AconfigFlagMustBeEnabled(
+ flagName = AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
+ flagValue = keyguardBottomAreaRefactor(),
+ ),
) +
classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
listOf(ComposeMustBeAvailable(), CompileTimeFlagMustBeEnabled())
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 05f125f..bff0b93 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -46,6 +46,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CallbackController;
import dagger.Lazy;
@@ -75,6 +76,7 @@
private final UserContextProvider mUserContextProvider;
private final UserTracker mUserTracker;
private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+ private final SystemUIDialog.Factory mDialogFactory;
protected static final String INTENT_UPDATE_STATE =
"com.android.systemui.screenrecord.UPDATE_STATE";
@@ -120,7 +122,8 @@
UserContextProvider userContextProvider,
Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
UserTracker userTracker,
- MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
+ MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
+ SystemUIDialog.Factory dialogFactory) {
mMainExecutor = mainExecutor;
mContext = context;
mFlags = flags;
@@ -129,6 +132,7 @@
mUserContextProvider = userContextProvider;
mUserTracker = userTracker;
mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
+ mDialogFactory = dialogFactory;
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
@@ -166,15 +170,14 @@
getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
- ? new ScreenRecordPermissionDialog(
- context,
- getHostUserHandle(),
- getHostUid(),
- /* controller= */ this,
- activityStarter,
- mUserContextProvider,
- onStartRecordingClicked,
- mMediaProjectionMetricsLogger)
+ ? mDialogFactory.create(new ScreenRecordPermissionDialogDelegate(
+ getHostUserHandle(),
+ getHostUid(),
+ /* controller= */ this,
+ activityStarter,
+ mUserContextProvider,
+ onStartRecordingClicked,
+ mMediaProjectionMetricsLogger))
: new ScreenRecordDialog(
context,
/* controller= */ this,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index f74234b..e57a0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -17,7 +17,6 @@
import android.app.Activity
import android.app.PendingIntent
-import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
@@ -35,17 +34,17 @@
import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
-import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
+import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionDialogDelegate
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
import com.android.systemui.mediaprojection.permission.SINGLE_APP
import com.android.systemui.mediaprojection.permission.ScreenShareOption
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.phone.SystemUIDialog
/** Dialog to select screen recording options */
-class ScreenRecordPermissionDialog(
- context: Context,
+class ScreenRecordPermissionDialogDelegate(
private val hostUserHandle: UserHandle,
private val hostUid: Int,
private val controller: RecordingController,
@@ -54,8 +53,7 @@
private val onStartRecordingClicked: Runnable?,
mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
) :
- BaseScreenSharePermissionDialog(
- context,
+ BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>(
createOptionList(),
appName = null,
hostUid = hostUid,
@@ -68,10 +66,10 @@
private lateinit var audioSwitch: Switch
private lateinit var options: Spinner
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
+ override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+ super.onCreate(dialog, savedInstanceState)
setDialogTitle(R.string.screenrecord_permission_dialog_title)
- setTitle(R.string.screenrecord_title)
+ dialog.setTitle(R.string.screenrecord_title)
setStartButtonText(R.string.screenrecord_permission_dialog_continue)
setStartButtonOnClickListener { v: View? ->
onStartRecordingClicked?.run()
@@ -79,7 +77,7 @@
requestScreenCapture(/* captureTarget= */ null)
}
if (selectedScreenShareOption.mode == SINGLE_APP) {
- val intent = Intent(context, MediaProjectionAppSelectorActivity::class.java)
+ val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// We can't start activity for result here so we use result receiver to get
@@ -96,22 +94,26 @@
intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
activityStarter.startActivity(intent, /* dismissShade= */ true)
}
- dismiss()
+ dialog.dismiss()
}
- setCancelButtonOnClickListener { dismiss() }
+ setCancelButtonOnClickListener { dialog.dismiss() }
initRecordOptionsView()
}
@LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
private fun initRecordOptionsView() {
- audioSwitch = requireViewById(R.id.screenrecord_audio_switch)
- tapsSwitch = requireViewById(R.id.screenrecord_taps_switch)
- tapsView = requireViewById(R.id.show_taps)
+ audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
+ tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
+ tapsView = dialog.requireViewById(R.id.show_taps)
updateTapsViewVisibility()
- options = requireViewById(R.id.screen_recording_options)
+ options = dialog.requireViewById(R.id.screen_recording_options)
val a: ArrayAdapter<*> =
- ScreenRecordingAdapter(context, android.R.layout.simple_spinner_dropdown_item, MODES)
+ ScreenRecordingAdapter(
+ dialog.context,
+ android.R.layout.simple_spinner_dropdown_item,
+ MODES
+ )
a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
options.adapter = a
options.setOnItemClickListenerInt { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
index 6fa592c..b30bc56 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java
@@ -16,7 +16,7 @@
package com.android.systemui.settings.brightness;
-import static com.android.systemui.flags.Flags.HAPTIC_BRIGHTNESS_SLIDER;
+import static com.android.systemui.Flags.hapticBrightnessSlider;
import android.content.Context;
import android.view.LayoutInflater;
@@ -32,7 +32,6 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.haptics.slider.SeekableSliderEventProducer;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -280,7 +279,6 @@
private final FalsingManager mFalsingManager;
private final UiEventLogger mUiEventLogger;
- private final FeatureFlagsClassic mFeatureFlags;
private final VibratorHelper mVibratorHelper;
private final SystemClock mSystemClock;
private final CoroutineDispatcher mMainDispatcher;
@@ -292,12 +290,10 @@
UiEventLogger uiEventLogger,
VibratorHelper vibratorHelper,
SystemClock clock,
- FeatureFlagsClassic featureFlags,
@Main CoroutineDispatcher mainDispatcher,
ActivityStarter activityStarter) {
mFalsingManager = falsingManager;
mUiEventLogger = uiEventLogger;
- mFeatureFlags = featureFlags;
mVibratorHelper = vibratorHelper;
mSystemClock = clock;
mMainDispatcher = mainDispatcher;
@@ -320,7 +316,7 @@
root.setActivityStarter(mActivityStarter);
BrightnessSliderHapticPlugin plugin;
- if (mFeatureFlags.isEnabled(HAPTIC_BRIGHTNESS_SLIDER)) {
+ if (hapticBrightnessSlider()) {
plugin = new BrightnessSliderHapticPluginImpl(
mVibratorHelper,
mSystemClock,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 85a4a7e..823caa0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -1070,7 +1071,7 @@
mQsController.init();
mShadeHeadsUpTracker.addTrackingHeadsUpListener(
mNotificationStackScrollLayoutController::setTrackingHeadsUp);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
setKeyguardBottomArea(mView.findViewById(R.id.keyguard_bottom_area));
}
@@ -1409,7 +1410,7 @@
updateViewControllers(userAvatarView, keyguardUserSwitcherView);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
mView.removeView(mKeyguardBottomArea);
@@ -1443,7 +1444,7 @@
mBarState);
}
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
setKeyguardBottomAreaVisibility(mBarState, false);
}
@@ -1456,7 +1457,7 @@
}
private void initBottomArea() {
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
mKeyguardBottomArea.init(
mKeyguardBottomAreaViewModel,
mFalsingManager,
@@ -1643,7 +1644,7 @@
mKeyguardStatusViewController.setLockscreenClockY(
mClockPositionAlgorithm.getExpandedPreferredClockY());
}
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
mKeyguardInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
} else {
@@ -2710,7 +2711,7 @@
float alpha = Math.min(expansionAlpha, 1 - mQsController.computeExpansionFraction());
alpha *= mBottomAreaShadeAlpha;
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
mKeyguardInteractor.setAlpha(alpha);
} else {
mKeyguardBottomAreaInteractor.setAlpha(alpha);
@@ -2936,7 +2937,7 @@
}
private void updateDozingVisibilities(boolean animate) {
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
mKeyguardInteractor.setAnimateDozingTransitions(animate);
} else {
mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
@@ -3144,7 +3145,7 @@
mDozing = dozing;
// TODO (b/) make listeners for this
mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
mKeyguardInteractor.setAnimateDozingTransitions(animate);
} else {
mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
@@ -4441,7 +4442,7 @@
goingToFullShade,
mBarState);
- if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (!keyguardBottomAreaRefactor()) {
setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
}
@@ -4698,7 +4699,7 @@
mKeyguardStatusViewController.setAlpha(alpha);
stackScroller.setAlpha(alpha);
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (keyguardBottomAreaRefactor()) {
mKeyguardInteractor.setAlpha(alpha);
} else {
mKeyguardBottomAreaInteractor.setAlpha(alpha);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 121aa42..e9779cd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -52,6 +52,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -59,6 +60,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
@@ -150,6 +152,7 @@
ConfigurationController configurationController,
KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
+ @Main Executor mainExecutor,
@Background Executor backgroundExecutor,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
@@ -158,7 +161,8 @@
AuthController authController,
Lazy<ShadeInteractor> shadeInteractorLazy,
ShadeWindowLogger logger,
- Lazy<SelectedUserInteractor> userInteractor) {
+ Lazy<SelectedUserInteractor> userInteractor,
+ UserTracker userTracker) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
mWindowManager = windowManager;
@@ -184,7 +188,9 @@
.addCallback(mStateListener,
SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
configurationController.addCallback(this);
-
+ if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
+ userTracker.addCallback(mUserTrackerCallback, mainExecutor);
+ }
float desiredPreferredRefreshRate = context.getResources()
.getInteger(R.integer.config_keyguardRefreshRate);
float actualPreferredRefreshRate = -1;
@@ -572,6 +578,7 @@
state.qsExpanded,
state.headsUpNotificationShowing,
state.lightRevealScrimOpaque,
+ state.isSwitchingUsers,
state.forceWindowCollapsed,
state.forceDozeBrightness,
state.forceUserActivity,
@@ -624,7 +631,8 @@
}
private void applyHasTopUi(NotificationShadeWindowState state) {
- mHasTopUiChanged = !state.componentsForcingTopUi.isEmpty() || isExpanded(state);
+ mHasTopUiChanged = !state.componentsForcingTopUi.isEmpty() || isExpanded(state)
+ || state.isSwitchingUsers;
}
private void applyNotTouchable(NotificationShadeWindowState state) {
@@ -954,4 +962,24 @@
setDreaming(isDreaming);
}
};
+
+ private final UserTracker.Callback mUserTrackerCallback = new UserTracker.Callback() {
+ @Override
+ public void onBeforeUserSwitching(int newUser) {
+ setIsSwitchingUsers(true);
+ }
+
+ @Override
+ public void onUserChanged(int newUser, Context userContext) {
+ setIsSwitchingUsers(false);
+ }
+
+ private void setIsSwitchingUsers(boolean isSwitchingUsers) {
+ if (mCurrentState.isSwitchingUsers == isSwitchingUsers) {
+ return;
+ }
+ mCurrentState.isSwitchingUsers = isSwitchingUsers;
+ apply(mCurrentState);
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index fbe164a..0b20170 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -40,6 +40,7 @@
@JvmField var qsExpanded: Boolean = false,
@JvmField var headsUpNotificationShowing: Boolean = false,
@JvmField var lightRevealScrimOpaque: Boolean = false,
+ @JvmField var isSwitchingUsers: Boolean = false,
@JvmField var forceWindowCollapsed: Boolean = false,
@JvmField var forceDozeBrightness: Boolean = false,
// TODO: forceUserActivity seems to be unused, delete?
@@ -78,6 +79,7 @@
qsExpanded.toString(),
headsUpNotificationShowing.toString(),
lightRevealScrimOpaque.toString(),
+ isSwitchingUsers.toString(),
forceWindowCollapsed.toString(),
forceDozeBrightness.toString(),
forceUserActivity.toString(),
@@ -117,6 +119,7 @@
qsExpanded: Boolean,
headsUpShowing: Boolean,
lightRevealScrimOpaque: Boolean,
+ isSwitchingUsers: Boolean,
forceCollapsed: Boolean,
forceDozeBrightness: Boolean,
forceUserActivity: Boolean,
@@ -145,6 +148,7 @@
this.qsExpanded = qsExpanded
this.headsUpNotificationShowing = headsUpShowing
this.lightRevealScrimOpaque = lightRevealScrimOpaque
+ this.isSwitchingUsers = isSwitchingUsers
this.forceWindowCollapsed = forceCollapsed
this.forceDozeBrightness = forceDozeBrightness
this.forceUserActivity = forceUserActivity
@@ -191,6 +195,7 @@
"qsExpanded",
"headsUpShowing",
"lightRevealScrimOpaque",
+ "isSwitchingUsers",
"forceCollapsed",
"forceDozeBrightness",
"forceUserActivity",
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index 03be88f..c59ef26 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,13 +18,15 @@
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.smartspace.SmartspacePrecondition
import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.smartspace.data.repository.SmartspaceRepositoryModule
import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import javax.inject.Named
-@Module(subcomponents = [SmartspaceViewComponent::class])
+@Module(subcomponents = [SmartspaceViewComponent::class],
+ includes = [SmartspaceRepositoryModule::class])
abstract class SmartspaceModule {
@Module
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
new file mode 100644
index 0000000..2fc0ec2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.smartspace.data.repository
+
+import android.app.smartspace.SmartspaceTarget
+import android.os.Parcelable
+import android.widget.RemoteViews
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
+
+interface SmartspaceRepository {
+ /** Whether [RemoteViews] are passed through smartspace targets. */
+ val isSmartspaceRemoteViewsEnabled: Boolean
+
+ /** Smartspace targets for the lockscreen surface. */
+ val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>>
+}
+
+@SysUISingleton
+class SmartspaceRepositoryImpl
+@Inject
+constructor(
+ private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+) : SmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener {
+
+ override val isSmartspaceRemoteViewsEnabled: Boolean
+ get() = android.app.smartspace.flags.Flags.remoteViews()
+
+ private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
+ MutableStateFlow(emptyList())
+ override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
+ _lockscreenSmartspaceTargets
+ .onStart {
+ lockscreenSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl)
+ }
+ .onCompletion {
+ lockscreenSmartspaceController.removeListener(
+ listener = this@SmartspaceRepositoryImpl
+ )
+ }
+
+ override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) {
+ targetsNullable?.let { targets ->
+ _lockscreenSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>()
+ }
+ ?: run { _lockscreenSmartspaceTargets.value = emptyList() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryModule.kt
new file mode 100644
index 0000000..c77bcc5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.smartspace.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface SmartspaceRepositoryModule {
+ @Binds fun smartspaceRepository(impl: SmartspaceRepositoryImpl): SmartspaceRepository
+}
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 65b798a..62c3e9e 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
@@ -18,7 +18,6 @@
import android.graphics.Color
import android.graphics.Rect
import android.view.View
-import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.ColorInt
import androidx.collection.ArrayMap
@@ -32,7 +31,6 @@
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColorLookup
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
@@ -52,6 +50,7 @@
import com.android.systemui.util.ui.value
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -88,14 +87,17 @@
return view.repeatWhenAttached {
lifecycleScope.run {
launch {
+ val iconColors =
+ viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
viewModel.icons.bindIcons(
view,
configuration,
configurationController,
- viewStore
- )
+ viewStore,
+ ) { _, sbiv ->
+ iconColors.collect { sbiv.updateTintForIcon(it, contrastColorUtil) }
+ }
}
- launch { viewModel.iconColors.bindIconColors(view, contrastColorUtil) }
launch { viewModel.bindIsolatedIcon(view, viewStore) }
launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
}
@@ -119,15 +121,17 @@
configuration,
configurationController,
viewStore,
- )
+ ) { _, sbiv ->
+ configuration
+ .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
+ .collect { tint ->
+ sbiv.staticDrawableColor = tint
+ sbiv.setDecorColor(tint)
+ }
+ }
}
launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
- launch {
- configuration
- .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
- .bindIconColors(view)
- }
}
}
}
@@ -137,31 +141,6 @@
collect(view::setAnimationsEnabled)
}
- /**
- * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
- * of the [children] of an [NotificationIconContainer].
- */
- private suspend fun Flow<NotificationIconColorLookup>.bindIconColors(
- view: NotificationIconContainer,
- contrastColorUtil: ContrastColorUtil,
- ) {
- mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
- .collect { iconLookup -> view.applyTint(iconLookup, contrastColorUtil) }
- }
-
- /**
- * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
- * of the [children] of an [NotificationIconContainer].
- */
- private suspend fun Flow<Int>.bindIconColors(view: NotificationIconContainer) {
- collect { tint ->
- view.children.filterIsInstance<StatusBarIconView>().forEach { icon ->
- icon.staticDrawableColor = tint
- icon.setDecorColor(tint)
- }
- }
- }
-
private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
view: NotificationIconContainer,
dozeParameters: DozeParameters,
@@ -208,12 +187,19 @@
}
}
- /** Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children]. */
+ /**
+ * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children].
+ *
+ * [bindIcon] will be invoked to bind a child [StatusBarIconView] to an icon associated with the
+ * given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
+ * view is to be unbound.
+ */
private suspend fun Flow<NotificationIconsViewData>.bindIcons(
view: NotificationIconContainer,
configuration: ConfigurationState,
configurationController: ConfigurationController,
viewStore: IconViewStore,
+ bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
): Unit = coroutineScope {
val iconSizeFlow: Flow<Int> =
configuration.getDimensionPixelSize(
@@ -242,6 +228,7 @@
}
}
+ val iconBindings = mutableMapOf<String, Job>()
var prevIcons = NotificationIconsViewData()
sample(layoutParams, ::Pair).collect {
(iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
@@ -261,15 +248,20 @@
}
iconsDiff.removed
- .mapNotNull { key -> childrenByNotifKey[key] }
- .forEach { child -> view.removeView(child) }
+ .mapNotNull { key -> childrenByNotifKey[key]?.let { key to it } }
+ .forEach { (key, child) ->
+ view.removeView(child)
+ iconBindings.remove(key)?.cancel()
+ }
- val toAdd = iconsDiff.added.map { viewStore.iconView(it.notifKey) }
- for ((i, sbiv) in toAdd.withIndex()) {
+ val toAdd = iconsDiff.added.map { it.notifKey to viewStore.iconView(it.notifKey) }
+ for ((i, keyAndView) in toAdd.withIndex()) {
+ val (key, sbiv) = keyAndView
// The view might still be transiently added if it was just removed
// and added again
view.removeTransientView(sbiv)
view.addView(sbiv, i, layoutParams)
+ iconBindings[key] = launch { bindIcon(key, sbiv) }
}
view.setChangingViewPositions(true)
@@ -292,16 +284,6 @@
// TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
// can be moved there and cleaned up.
- private fun ViewGroup.applyTint(
- iconColors: NotificationIconColors,
- contrastColorUtil: ContrastColorUtil,
- ) {
- children
- .filterIsInstance<StatusBarIconView>()
- .filter { it.width != 0 }
- .forEach { iv -> iv.updateTintForIcon(iconColors, contrastColorUtil) }
- }
-
private fun StatusBarIconView.updateTintForIcon(
iconColors: NotificationIconColors,
contrastColorUtil: ContrastColorUtil,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 966ff35..ec90a8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -90,10 +90,10 @@
} else {
setWillBeGone(true);
}
- setContentVisible(visible, true /* animate */, null /* runAfter */);
+ setContentVisible(visible, true /* animate */, null /* onAnimationEnded */);
} else {
setVisibility(visible ? VISIBLE : GONE);
- setContentVisible(visible, false /* animate */, null /* runAfter */);
+ setContentVisible(visible, false /* animate */, null /* onAnimationEnded */);
setWillBeGone(false);
notifyHeightChanged(false /* needsAnimation */);
}
@@ -108,7 +108,7 @@
* Change content visibility to {@code visible}, animated.
*/
public void setContentVisibleAnimated(boolean visible) {
- setContentVisible(visible, true /* animate */, null /* runAfter */);
+ setContentVisible(visible, true /* animate */, null /* onAnimationEnded */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.kt
new file mode 100644
index 0000000..44387c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsLiveDataRefactor.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.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notifications live data store refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationsLiveDataStoreRefactor {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_LIVE_DATA_STORE_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.notificationsLiveDataStoreRefactor()
+
+ /**
+ * 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)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index 5c1149b..580431a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -53,7 +53,7 @@
mContents = requireViewById(R.id.content);
bindContents();
super.onFinishInflate();
- setVisible(true /* nowVisible */, false /* animate */);
+ setVisible(true /* visible */, false /* animate */);
}
private void bindContents() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 13c9964..9471574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -100,6 +100,17 @@
val isAnySimSecure: Flow<Boolean>
/**
+ * Returns whether any active SIM on the device is in
+ * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
+ * [android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED].
+ *
+ * Note: Unfortunately, we cannot name this [isAnySimSecure] due to a conflict with the flow
+ * name above (Java code-gen is having issues with it).
+ */
+ fun getIsAnySimSecure(): Boolean
+
+ /**
* Checks if any subscription has [android.telephony.TelephonyManager.getEmergencyCallbackMode]
* == true
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 87dd17e..8a8e33e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -152,6 +152,7 @@
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+ override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
override val defaultDataSubId: StateFlow<Int> =
activeRepo
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 2ecd435..2b3c632 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -136,7 +136,8 @@
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
- override val isAnySimSecure: Flow<Boolean> = flowOf(false)
+ override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure())
+ override fun getIsAnySimSecure(): Boolean = false
override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index cf7bf86..2a510e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -262,7 +262,7 @@
object : KeyguardUpdateMonitorCallback() {
override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) {
logger.logOnSimStateChanged()
- trySend(keyguardUpdateMonitor.isSimPinSecure)
+ trySend(getIsAnySimSecure())
}
}
keyguardUpdateMonitor.registerCallback(callback)
@@ -277,6 +277,8 @@
)
.distinctUntilChanged()
+ override fun getIsAnySimSecure() = keyguardUpdateMonitor.isSimPinSecure
+
override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
getOrCreateRepoForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt b/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt
index 3b300249..b1b6014 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/telephony/data/repository/TelephonyRepository.kt
@@ -17,23 +17,40 @@
package com.android.systemui.telephony.data.repository
+import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
+import android.telecom.TelecomManager
import android.telephony.Annotation
import android.telephony.TelephonyCallback
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.telephony.TelephonyListenerManager
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Defines interface for classes that encapsulate _some_ telephony-related state. */
interface TelephonyRepository {
/** The state of the current call. */
@Annotation.CallState val callState: Flow<Int>
+ /**
+ * Whether there is an ongoing phone call (can be in dialing, ringing, active or holding states)
+ * originating from either a manager or self-managed {@link ConnectionService}.
+ */
+ val isInCall: StateFlow<Boolean>
+
/** Whether the device has a radio that can be used for telephony. */
val hasTelephonyRadio: Boolean
}
@@ -49,18 +66,35 @@
class TelephonyRepositoryImpl
@Inject
constructor(
+ @Application private val applicationScope: CoroutineScope,
@Application private val applicationContext: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
private val manager: TelephonyListenerManager,
+ private val telecomManager: TelecomManager?,
) : TelephonyRepository {
+
@Annotation.CallState
override val callState: Flow<Int> = conflatedCallbackFlow {
- val listener = TelephonyCallback.CallStateListener { state -> trySend(state) }
+ val listener = TelephonyCallback.CallStateListener(::trySend)
manager.addCallStateListener(listener)
awaitClose { manager.removeCallStateListener(listener) }
}
- override val hasTelephonyRadio: Boolean
- get() = applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+ @SuppressLint("MissingPermission")
+ override val isInCall: StateFlow<Boolean> =
+ if (telecomManager == null) {
+ flowOf(false)
+ } else {
+ callState.map { withContext(backgroundDispatcher) { telecomManager.isInCall } }
+ }
+ .stateIn(
+ applicationScope,
+ SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ override val hasTelephonyRadio =
+ applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
}
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt b/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
index 4642f55..4b0e5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
@@ -22,17 +22,18 @@
import com.android.systemui.telephony.data.repository.TelephonyRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/** Hosts business logic related to telephony. */
@SysUISingleton
class TelephonyInteractor
@Inject
constructor(
- private val repository: TelephonyRepository,
+ repository: TelephonyRepository,
) {
@Annotation.CallState val callState: Flow<Int> = repository.callState
- /** Whether the device has a radio that can be used for telephony. */
- val hasTelephonyRadio: Boolean
- get() = repository.hasTelephonyRadio
+ val isInCall: StateFlow<Boolean> = repository.isInCall
+
+ val hasTelephonyRadio: Boolean = repository.hasTelephonyRadio
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
index 771a8c8..799e5af 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/LockscreenFragment.java
@@ -48,6 +48,8 @@
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
import com.android.systemui.tuner.ShortcutParser.Shortcut;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
import java.util.ArrayList;
import java.util.Map;
@@ -69,6 +71,9 @@
private TunerService mTunerService;
private Handler mHandler;
+ // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so
+ // explicitly declare references per usage in `R.xml.lockscreen_settings`. See b/120445169.
+ @UsesReflection(@KeepTarget(classConstant = ShortcutPicker.class))
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
mTunerService = Dependency.get(TunerService.class);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
index 32b1b26..8d85999 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/OtherPrefs.java
@@ -19,8 +19,13 @@
import androidx.preference.PreferenceFragment;
import com.android.systemui.res.R;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
public class OtherPrefs extends PreferenceFragment {
+ // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so
+ // explicitly declare references per usage in `R.xml.other_settings`. See b/120445169.
+ @UsesReflection(@KeepTarget(classConstant = PowerNotificationControlsFragment.class))
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.other_settings);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 9cc526a..873b6d5 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -35,6 +35,8 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.res.R;
import com.android.systemui.shared.plugins.PluginPrefs;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
public class TunerFragment extends PreferenceFragment {
@@ -77,6 +79,13 @@
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}
+ // aapt doesn't generate keep rules for android:fragment references in <Preference> tags, so
+ // explicitly declare references per usage in `R.xml.tuner_prefs`. See b/120445169.
+ @UsesReflection({
+ @KeepTarget(classConstant = LockscreenFragment.class),
+ @KeepTarget(classConstant = NavBarTuner.class),
+ @KeepTarget(classConstant = PluginFragment.class),
+ })
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.tuner_prefs);
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 78fb7a4..3ed05aa 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -34,10 +34,10 @@
@UserIdInt
@JvmOverloads
fun getSelectedUserId(bypassFlag: Boolean = false): Int {
- if (bypassFlag || flags.isEnabled(REFACTOR_GETCURRENTUSER)) {
- return repository.getSelectedUserInfo().id
+ return if (bypassFlag || flags.isEnabled(REFACTOR_GETCURRENTUSER)) {
+ repository.getSelectedUserInfo().id
} else {
- return KeyguardUpdateMonitor.getCurrentUser()
+ KeyguardUpdateMonitor.getCurrentUser()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/reference/ReferenceModule.kt b/packages/SystemUI/src/com/android/systemui/util/reference/ReferenceModule.kt
new file mode 100644
index 0000000..e7a91e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/reference/ReferenceModule.kt
@@ -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.
+ */
+
+package com.android.systemui.util.reference
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class ReferenceModule {
+ @Binds
+ abstract fun bindWeakReferenceFactory(impl: WeakReferenceFactoryImpl): WeakReferenceFactory
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/reference/WeakReferenceFactory.kt b/packages/SystemUI/src/com/android/systemui/util/reference/WeakReferenceFactory.kt
new file mode 100644
index 0000000..658f040
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/reference/WeakReferenceFactory.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.reference
+
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+interface WeakReferenceFactory {
+ fun <T> create(referent: T): WeakReference<T>
+}
+
+class WeakReferenceFactoryImpl @Inject constructor() : WeakReferenceFactory {
+ override fun <T> create(referent: T): WeakReference<T> {
+ return WeakReference(referent)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
index 0cb913b..fd50f15 100644
--- a/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
+++ b/packages/SystemUI/tests/src/com/android/TestMocksModule.kt
@@ -38,6 +38,7 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -66,6 +67,7 @@
@Module(includes = [TestMocksModule.Bindings::class])
data class TestMocksModule(
@get:Provides val activityStarter: ActivityStarter = mock(),
+ @get:Provides val activityManagerWrapper: ActivityManagerWrapper = mock(),
@get:Provides val ambientState: AmbientState = mock(),
@get:Provides val bubbles: Optional<Bubbles> = Optional.of(mock()),
@get:Provides val darkIconDispatcher: DarkIconDispatcher = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index c61b11a..9a95b17 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -48,7 +48,6 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class EmergencyButtonControllerTest : SysuiTestCase() {
- lateinit var underTest: EmergencyButtonController
@Mock lateinit var emergencyButton: EmergencyButton
@Mock lateinit var configurationController: ConfigurationController
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -61,10 +60,13 @@
@Mock lateinit var lockPatternUtils: LockPatternUtils
@Mock lateinit var packageManager: PackageManager
@Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
+
val fakeSystemClock = FakeSystemClock()
val mainExecutor = FakeExecutor(fakeSystemClock)
val backgroundExecutor = FakeExecutor(fakeSystemClock)
+ lateinit var underTest: EmergencyButtonController
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -73,7 +75,6 @@
emergencyButton,
configurationController,
keyguardUpdateMonitor,
- telephonyManager,
powerManager,
activityTaskManager,
shadeController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index aabe633..a38ba00 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.plugins.ClockAnimations;
@@ -204,7 +205,8 @@
mock(AlwaysOnDisplayNotificationIconViewStore.class),
KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
mKeyguardClockInteractor,
- mFakeFeatureFlags
+ mFakeFeatureFlags,
+ mock(InWindowLauncherUnlockAnimationManager.class)
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index d61ca69..1d4f2cb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -21,7 +21,6 @@
import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -39,6 +38,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
@@ -148,9 +148,10 @@
when(mStatusBarStateController.isDozing()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
+
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
- mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
index e3a2c59..d77a80a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
@@ -31,42 +31,60 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
/** Tests for {@link RadiiAnimator}. */
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class RadiiAnimatorTest extends SysuiTestCase {
float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT];
+ final AtomicBoolean mAnimationStarted = new AtomicBoolean(false);
+ final AtomicBoolean mAnimationStopped = new AtomicBoolean(false);
+ final IRadiiAnimationListener mRadiiAnimationListener = new IRadiiAnimationListener() {
+ @Override
+ public void onRadiiAnimationUpdate(float[] radii) {
+ mResultRadii = radii;
+ }
+
+ @Override
+ public void onRadiiAnimationStart() {
+ mAnimationStarted.set(true);
+ }
+
+ @Override
+ public void onRadiiAnimationStop() {
+ mAnimationStopped.set(true);
+ }
+ };
@Test
public void constructor() {
final float[] radii = generateRadii(0.0f);
- final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, newRadii -> {});
-
+ final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, mRadiiAnimationListener);
assertThat(radiiAnimator.evaluate(0.0f)).isEqualTo(radii);
}
@Test
- public void skip_updates_to_end() {
+ public void skipAnimation_updatesToEnd() {
final float[] startRadii = generateRadii(0.0f);
final float[] endRadii = generateRadii(1.0f);
final RadiiAnimator radiiAnimator = setupAnimator(startRadii);
+ mAnimationStarted.set(false);
+ mAnimationStopped.set(false);
new Handler(Looper.getMainLooper()).post(() -> radiiAnimator.startAnimation(endRadii));
- TestUtils.waitForCondition(radiiAnimator::isStarted, "Animation did not start.");
+ TestUtils.waitForCondition(mAnimationStarted::get, "Animation did not start.");
TestUtils.waitForCondition(() -> Arrays.equals(radiiAnimator.evaluate(0.0f), startRadii)
- && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii),
+ && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii),
"Animator did not initialize to start and end values");
-
new Handler(Looper.getMainLooper()).post(radiiAnimator::skipAnimationToEnd);
- TestUtils.waitForCondition(
- () -> !radiiAnimator.isStarted(), "Animation did not end.");
+ TestUtils.waitForCondition(mAnimationStopped::get, "Animation did not stop.");
assertThat(mResultRadii).usingTolerance(0.001).containsExactly(endRadii);
}
@Test
- public void animation_can_repeat() {
+ public void finishedAnimation_canRepeat() {
final float[] startRadii = generateRadii(0.0f);
final float[] midRadii = generateRadii(1.0f);
final float[] endRadii = generateRadii(2.0f);
@@ -88,15 +106,15 @@
private RadiiAnimator setupAnimator(float[] startRadii) {
mResultRadii = new float[RadiiAnimator.RADII_COUNT];
- return new RadiiAnimator(startRadii,
- newRadii -> mResultRadii = newRadii);
+ return new RadiiAnimator(startRadii, mRadiiAnimationListener);
}
private void playAndSkipAnimation(RadiiAnimator animator, float[] endRadii) {
+ mAnimationStarted.set(false);
+ mAnimationStopped.set(false);
new Handler(Looper.getMainLooper()).post(() -> animator.startAnimation(endRadii));
- TestUtils.waitForCondition(animator::isStarted, "Animation did not start.");
+ TestUtils.waitForCondition(mAnimationStarted::get, "Animation did not start.");
new Handler(Looper.getMainLooper()).post(animator::skipAnimationToEnd);
- TestUtils.waitForCondition(
- () -> !animator.isStarted(), "Animation did not end.");
+ TestUtils.waitForCondition(mAnimationStopped::get, "Animation did not stop.");
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
index a049191..44c57f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
@@ -2,15 +2,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -18,32 +16,34 @@
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardBouncerRepositoryTest : SysuiTestCase() {
@Mock private lateinit var systemClock: SystemClock
- @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback
@Mock private lateinit var bouncerLogger: TableLogBuffer
+
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
+
lateinit var underTest: KeyguardBouncerRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- val testCoroutineScope = TestCoroutineScope()
underTest =
KeyguardBouncerRepositoryImpl(
systemClock,
- testCoroutineScope,
+ testScope.backgroundScope,
bouncerLogger,
)
}
@Test
- fun changingFlowValueTriggersLogging() = runBlocking {
- underTest.setPrimaryShow(true)
- Mockito.verify(bouncerLogger)
- .logChange(eq(""), eq("PrimaryBouncerShow"), value = eq(false), any())
- }
+ fun changingFlowValueTriggersLogging() =
+ testScope.runTest {
+ underTest.setPrimaryShow(true)
+ Mockito.verify(bouncerLogger)
+ .logChange(eq(""), eq("PrimaryBouncerShow"), value = eq(false), any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 2c97809..c159b66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -45,6 +45,7 @@
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val authenticationInteractor = utils.authenticationInteractor()
+ private val actionButtonInteractor = utils.bouncerActionButtonInteractor()
private val deviceEntryInteractor =
utils.deviceEntryInteractor(
authenticationInteractor = authenticationInteractor,
@@ -60,6 +61,7 @@
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
+ actionButtonInteractor = actionButtonInteractor,
)
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index b75f3e0..2cc8f0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -52,8 +52,7 @@
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyguardBouncerViewModelTest : SysuiTestCase() {
- lateinit var underTest: KeyguardBouncerViewModel
- lateinit var bouncerInteractor: PrimaryBouncerInteractor
+
@Mock lateinit var bouncerView: BouncerView
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
@@ -62,9 +61,13 @@
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ lateinit var bouncerInteractor: PrimaryBouncerInteractor
private val mainHandler = FakeHandler(Looper.getMainLooper())
val repository = FakeKeyguardBouncerRepository()
+ lateinit var underTest: KeyguardBouncerViewModel
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index ba8dcef..390742031 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -61,7 +61,9 @@
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
+ actionButtonInteractor = utils.bouncerActionButtonInteractor(),
)
+
private val underTest =
PasswordBouncerViewModel(
viewModelScope = testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index bfaa6ed..47db4f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -64,6 +64,7 @@
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
+ actionButtonInteractor = utils.bouncerActionButtonInteractor(),
)
private val underTest =
PatternBouncerViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 7873899..3ddac7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -63,6 +63,7 @@
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
authenticationInteractor = authenticationInteractor,
+ actionButtonInteractor = utils.bouncerActionButtonInteractor(),
)
private val underTest =
PinBouncerViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
new file mode 100644
index 0000000..14ec4d4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -0,0 +1,163 @@
+/*
+ * 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.data.db
+
+import android.content.ComponentName
+import androidx.room.Room
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.lifecycle.InstantTaskExecutorRule
+import com.google.common.truth.Truth.assertThat
+import java.io.IOException
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalWidgetDaoTest : SysuiTestCase() {
+ @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule()
+
+ private lateinit var db: CommunalDatabase
+ private lateinit var communalWidgetDao: CommunalWidgetDao
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ @Throws(IOException::class)
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ db =
+ Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java)
+ .allowMainThreadQueries()
+ .build()
+ communalWidgetDao = db.communalWidgetDao()
+ }
+
+ @After
+ @Throws(IOException::class)
+ fun teardown() {
+ db.close()
+ }
+
+ @Test
+ fun addWidget_readValueInDb() =
+ testScope.runTest {
+ val (widgetId, provider, priority) = widgetInfo1
+ communalWidgetDao.addWidget(
+ widgetId = widgetId,
+ provider = provider,
+ priority = priority,
+ )
+ val entry = communalWidgetDao.getWidgetByIdNow(id = 1)
+ assertThat(entry).isEqualTo(communalWidgetItemEntry1)
+ }
+
+ @Test
+ fun addWidget_emitsActiveWidgetsInDb(): Unit =
+ testScope.runTest {
+ val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
+ val widgets = collectLastValue(communalWidgetDao.getWidgets())
+ widgetsToAdd.forEach {
+ val (widgetId, provider, priority) = it
+ communalWidgetDao.addWidget(
+ widgetId = widgetId,
+ provider = provider,
+ priority = priority,
+ )
+ }
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry1,
+ communalWidgetItemEntry1,
+ communalItemRankEntry2,
+ communalWidgetItemEntry2
+ )
+ }
+
+ @Test
+ fun deleteWidget_emitsActiveWidgetsInDb() =
+ testScope.runTest {
+ val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
+ val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+ widgetsToAdd.forEach {
+ val (widgetId, provider, priority) = it
+ communalWidgetDao.addWidget(
+ widgetId = widgetId,
+ provider = provider,
+ priority = priority,
+ )
+ }
+ assertThat(widgets())
+ .containsExactly(
+ communalItemRankEntry1,
+ communalWidgetItemEntry1,
+ communalItemRankEntry2,
+ communalWidgetItemEntry2
+ )
+
+ communalWidgetDao.deleteWidgetById(communalWidgetItemEntry1.widgetId)
+ assertThat(widgets()).containsExactly(communalItemRankEntry2, communalWidgetItemEntry2)
+ }
+
+ data class FakeWidgetMetadata(
+ val widgetId: Int,
+ val provider: ComponentName,
+ val priority: Int
+ )
+
+ companion object {
+ val widgetInfo1 =
+ FakeWidgetMetadata(
+ widgetId = 1,
+ provider = ComponentName("pk_name", "cls_name_1"),
+ priority = 1
+ )
+ val widgetInfo2 =
+ FakeWidgetMetadata(
+ widgetId = 2,
+ provider = ComponentName("pk_name", "cls_name_2"),
+ priority = 2
+ )
+ val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.priority)
+ val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.priority)
+ val communalWidgetItemEntry1 =
+ CommunalWidgetItem(
+ uid = 1L,
+ widgetId = widgetInfo1.widgetId,
+ componentName = widgetInfo1.provider.flattenToString(),
+ itemId = communalItemRankEntry1.uid,
+ )
+ val communalWidgetItemEntry2 =
+ CommunalWidgetItem(
+ uid = 2L,
+ widgetId = widgetInfo2.widgetId,
+ componentName = widgetInfo2.provider.flattenToString(),
+ itemId = communalItemRankEntry2.uid,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
new file mode 100644
index 0000000..455f986
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalMediaRepositoryImplTest : SysuiTestCase() {
+ @Mock private lateinit var mediaDataManager: MediaDataManager
+ @Mock private lateinit var mediaData: MediaData
+
+ private val mediaDataListenerCaptor: KotlinArgumentCaptor<MediaDataManager.Listener> by lazy {
+ KotlinArgumentCaptor(MediaDataManager.Listener::class.java)
+ }
+
+ private lateinit var mediaRepository: CommunalMediaRepository
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun mediaPlaying_defaultsToFalse() =
+ testScope.runTest {
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+ }
+
+ @Test
+ fun mediaPlaying_emitsInitialValue() =
+ testScope.runTest {
+ // Start with media available.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+ }
+
+ @Test
+ fun mediaPlaying_updatesWhenMediaDataLoaded() =
+ testScope.runTest {
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ // Initial value is false.
+ var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+
+ // Listener is added
+ verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+
+ // Change to media available and notify the listener.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+ mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
+
+ // mediaPlaying now returns true.
+ isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+ }
+
+ @Test
+ fun mediaPlaying_updatesWhenMediaDataRemoved() =
+ testScope.runTest {
+ // Start with media available.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ // Initial value is true.
+ var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+
+ // Listener is added.
+ verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+
+ // Change to media unavailable and notify the listener.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false)
+ mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
+
+ // mediaPlaying now returns false.
+ isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index fcb191b..ca8316d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -1,9 +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.
+ */
+
package com.android.systemui.communal.data.repository
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.BroadcastReceiver
+import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.UserHandle
import android.os.UserManager
@@ -11,8 +28,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.communal.data.model.CommunalWidgetMetadata
-import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.data.db.CommunalItemRank
+import com.android.systemui.communal.data.db.CommunalWidgetDao
+import com.android.systemui.communal.data.db.CommunalWidgetItem
+import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FeatureFlagsClassic
@@ -28,6 +47,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -66,9 +86,9 @@
@Mock private lateinit var providerInfoA: AppWidgetProviderInfo
- @Mock private lateinit var providerInfoB: AppWidgetProviderInfo
+ @Mock private lateinit var communalWidgetHost: CommunalWidgetHost
- @Mock private lateinit var providerInfoC: AppWidgetProviderInfo
+ @Mock private lateinit var communalWidgetDao: CommunalWidgetDao
private lateinit var communalRepository: FakeCommunalRepository
@@ -103,6 +123,92 @@
}
@Test
+ fun neverQueryDbForWidgets_whenFeatureIsDisabled() =
+ testScope.runTest {
+ communalEnabled(false)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.communalWidgets)()
+ runCurrent()
+
+ verify(communalWidgetDao, Mockito.never()).getWidgets()
+ }
+
+ @Test
+ fun neverQueryDbForWidgets_whenFeatureEnabled_andUserLocked() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ collectLastValue(repository.communalWidgets)()
+ runCurrent()
+
+ verify(communalWidgetDao, Mockito.never()).getWidgets()
+ }
+
+ @Test
+ fun communalWidgets_whenUserUnlocked_queryWidgetsFromDb() =
+ testScope.runTest {
+ userUnlocked(false)
+ val repository = initCommunalWidgetRepository()
+ val communalWidgets = collectLastValue(repository.communalWidgets)
+ communalWidgets()
+ runCurrent()
+ val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1)
+ val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L)
+ whenever(communalWidgetDao.getWidgets())
+ .thenReturn(flowOf(mapOf(communalItemRankEntry to communalWidgetItemEntry)))
+ whenever(appWidgetManager.getAppWidgetInfo(anyInt())).thenReturn(providerInfoA)
+
+ userUnlocked(true)
+ installedProviders(listOf(stopwatchProviderInfo))
+ broadcastReceiverUpdate()
+ runCurrent()
+
+ verify(communalWidgetDao).getWidgets()
+ assertThat(communalWidgets())
+ .containsExactly(
+ CommunalWidgetContentModel(
+ appWidgetId = communalWidgetItemEntry.widgetId,
+ providerInfo = providerInfoA,
+ priority = communalItemRankEntry.rank,
+ )
+ )
+ }
+
+ @Test
+ fun addWidget_allocateId_bindWidget_andAddToDb() =
+ testScope.runTest {
+ userUnlocked(true)
+ val repository = initCommunalWidgetRepository()
+ runCurrent()
+
+ val provider = ComponentName("pkg_name", "cls_name")
+ val id = 1
+ val priority = 1
+ whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>()))
+ .thenReturn(id)
+ repository.addWidget(provider, priority)
+ runCurrent()
+
+ verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+ verify(communalWidgetDao).addWidget(id, provider, priority)
+ }
+
+ @Test
+ fun deleteWidget_removeWidgetId_andDeleteFromDb() =
+ testScope.runTest {
+ userUnlocked(true)
+ val repository = initCommunalWidgetRepository()
+ runCurrent()
+
+ val id = 1
+ repository.deleteWidget(id)
+ runCurrent()
+
+ verify(communalWidgetDao).deleteWidgetById(id)
+ verify(appWidgetHost).deleteAppWidgetId(id)
+ }
+
+ @Test
fun broadcastReceiver_communalDisabled_doNotRegisterUserUnlockedBroadcastReceiver() =
testScope.runTest {
communalEnabled(false)
@@ -183,34 +289,6 @@
}
@Test
- fun appWidgetId_userLockedAgainAfterProviderInfoAvailable_deleteAppWidgetId() =
- testScope.runTest {
- whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(123456)
- userUnlocked(false)
- val repository = initCommunalWidgetRepository()
- val lastStopwatchProviderInfo = collectLastValue(repository.stopwatchAppWidgetInfo)
- assertThat(lastStopwatchProviderInfo()).isNull()
-
- // User unlocks
- userUnlocked(true)
- installedProviders(listOf(stopwatchProviderInfo))
- broadcastReceiverUpdate()
-
- // Verify app widget id allocated
- assertThat(lastStopwatchProviderInfo()?.appWidgetId).isEqualTo(123456)
- verify(appWidgetHost).allocateAppWidgetId()
- verify(appWidgetHost, Mockito.never()).deleteAppWidgetId(anyInt())
-
- // User locked again
- userUnlocked(false)
- broadcastReceiverUpdate()
-
- // Verify app widget id deleted
- assertThat(lastStopwatchProviderInfo()).isNull()
- verify(appWidgetHost).deleteAppWidgetId(123456)
- }
-
- @Test
fun appWidgetHost_userUnlocked_startListening() =
testScope.runTest {
userUnlocked(false)
@@ -246,95 +324,16 @@
verify(appWidgetHost).stopListening()
}
- @Test
- fun getCommunalWidgetAllowList_onInit() {
- testScope.runTest {
- val repository = initCommunalWidgetRepository()
- val communalWidgetAllowlist = repository.communalWidgetAllowlist
- assertThat(
- listOf(
- CommunalWidgetMetadata(
- componentName = fakeAllowlist[0],
- priority = 3,
- sizes = listOf(CommunalContentSize.HALF),
- ),
- CommunalWidgetMetadata(
- componentName = fakeAllowlist[1],
- priority = 2,
- sizes = listOf(CommunalContentSize.HALF),
- ),
- CommunalWidgetMetadata(
- componentName = fakeAllowlist[2],
- priority = 1,
- sizes = listOf(CommunalContentSize.HALF),
- ),
- )
- )
- .containsExactly(*communalWidgetAllowlist.toTypedArray())
- }
- }
-
- // This behavior is temporary before the local database is set up.
- @Test
- fun communalWidgets_withPreviouslyBoundWidgets_removeEachBinding() =
- testScope.runTest {
- whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(1, 2, 3)
- setAppWidgetIds(listOf(1, 2, 3))
- whenever(appWidgetManager.getAppWidgetInfo(anyInt())).thenReturn(providerInfoA)
- userUnlocked(true)
-
- val repository = initCommunalWidgetRepository()
-
- collectLastValue(repository.communalWidgets)()
-
- verify(appWidgetHost).deleteAppWidgetId(1)
- verify(appWidgetHost).deleteAppWidgetId(2)
- verify(appWidgetHost).deleteAppWidgetId(3)
- }
-
- @Test
- fun communalWidgets_allowlistNotEmpty_bindEachWidgetFromTheAllowlist() =
- testScope.runTest {
- whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(0, 1, 2)
- userUnlocked(true)
-
- whenever(appWidgetManager.getAppWidgetInfo(0)).thenReturn(providerInfoA)
- whenever(appWidgetManager.getAppWidgetInfo(1)).thenReturn(providerInfoB)
- whenever(appWidgetManager.getAppWidgetInfo(2)).thenReturn(providerInfoC)
-
- val repository = initCommunalWidgetRepository()
-
- val inventory by collectLastValue(repository.communalWidgets)
-
- assertThat(
- listOf(
- CommunalWidgetContentModel(
- appWidgetId = 0,
- providerInfo = providerInfoA,
- priority = 3,
- ),
- CommunalWidgetContentModel(
- appWidgetId = 1,
- providerInfo = providerInfoB,
- priority = 2,
- ),
- CommunalWidgetContentModel(
- appWidgetId = 2,
- providerInfo = providerInfoC,
- priority = 1,
- ),
- )
- )
- .containsExactly(*inventory!!.toTypedArray())
- }
-
private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
return CommunalWidgetRepositoryImpl(
- context,
appWidgetManager,
appWidgetHost,
+ testScope.backgroundScope,
+ testDispatcher,
broadcastDispatcher,
communalRepository,
+ communalWidgetHost,
+ communalWidgetDao,
packageManager,
userManager,
userTracker,
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 2f17b6f..08d54c0 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
@@ -17,14 +17,26 @@
package com.android.systemui.communal.domain.interactor
+import android.app.smartspace.SmartspaceTarget
+import android.provider.Settings
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
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.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
@@ -34,6 +46,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
@@ -44,24 +57,37 @@
private lateinit var testScope: TestScope
+ private lateinit var tutorialRepository: FakeCommunalTutorialRepository
private lateinit var communalRepository: FakeCommunalRepository
+ private lateinit var mediaRepository: FakeCommunalMediaRepository
private lateinit var widgetRepository: FakeCommunalWidgetRepository
- private lateinit var interactor: CommunalInteractor
+ private lateinit var smartspaceRepository: FakeSmartspaceRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+
+ private lateinit var underTest: CommunalInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
- communalRepository = FakeCommunalRepository()
- widgetRepository = FakeCommunalWidgetRepository()
- interactor = CommunalInteractor(communalRepository, widgetRepository)
+
+ val withDeps = CommunalInteractorFactory.create()
+
+ tutorialRepository = withDeps.tutorialRepository
+ communalRepository = withDeps.communalRepository
+ mediaRepository = withDeps.mediaRepository
+ widgetRepository = withDeps.widgetRepository
+ smartspaceRepository = withDeps.smartspaceRepository
+ keyguardRepository = withDeps.keyguardRepository
+
+ underTest = withDeps.communalInteractor
}
@Test
fun appWidgetInfoFlow() =
testScope.runTest {
- val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
+ val lastAppWidgetInfo = collectLastValue(underTest.appWidgetInfo)
runCurrent()
assertThat(lastAppWidgetInfo()).isNull()
@@ -74,31 +100,215 @@
fun communalEnabled() =
testScope.runTest {
communalRepository.setIsCommunalEnabled(true)
-
- val interactor = CommunalInteractor(communalRepository, widgetRepository)
- assertThat(interactor.isCommunalEnabled).isTrue()
+ assertThat(underTest.isCommunalEnabled).isTrue()
}
@Test
fun communalDisabled() =
testScope.runTest {
communalRepository.setIsCommunalEnabled(false)
+ assertThat(underTest.isCommunalEnabled).isFalse()
+ }
- val interactor = CommunalInteractor(communalRepository, widgetRepository)
- assertThat(interactor.isCommunalEnabled).isFalse()
+ @Test
+ fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial not started.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(
+ Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+ )
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ assertThat(communalContent!!).isNotEmpty()
+ communalContent!!.forEach { model ->
+ assertThat(model is CommunalContentModel.Tutorial).isTrue()
+ }
+ }
+
+ @Test
+ fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Widgets are available.
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 2,
+ priority = 10,
+ providerInfo = mock(),
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ assertThat(communalContent!!).isNotEmpty()
+ communalContent!!.forEachIndexed { index, model ->
+ assertThat((model as CommunalContentModel.Widget).appWidgetId)
+ .isEqualTo(widgets[index].appWidgetId)
+ }
+ }
+
+ @Test
+ fun smartspace_onlyShowTimersWithRemoteViews() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Not a timer
+ val target1 = mock(SmartspaceTarget::class.java)
+ whenever(target1.smartspaceTargetId).thenReturn("target1")
+ whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_WEATHER)
+ whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
+
+ // Does not have RemoteViews
+ val target2 = mock(SmartspaceTarget::class.java)
+ whenever(target1.smartspaceTargetId).thenReturn("target2")
+ whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target1.remoteViews).thenReturn(null)
+
+ // Timer and has RemoteViews
+ val target3 = mock(SmartspaceTarget::class.java)
+ whenever(target1.smartspaceTargetId).thenReturn("target3")
+ whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
+
+ val targets = listOf(target1, target2, target3)
+ smartspaceRepository.setLockscreenSmartspaceTargets(targets)
+
+ val communalContent by collectLastValue(underTest.communalContent)
+ assertThat(communalContent?.size).isEqualTo(1)
+ assertThat(communalContent?.get(0)?.key).isEqualTo("smartspace_target3")
+ }
+
+ @Test
+ fun smartspace_smartspaceAndWidgetsAvailable_showSmartspaceAndWidgetContent() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Widgets available.
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = mock(),
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Smartspace available.
+ val target = mock(SmartspaceTarget::class.java)
+ whenever(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
+ smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ assertThat(communalContent?.size).isEqualTo(3)
+ assertThat(communalContent?.get(0)?.key).isEqualTo("smartspace_target")
+ assertThat(communalContent?.get(1)?.key).isEqualTo("widget_0")
+ assertThat(communalContent?.get(2)?.key).isEqualTo("widget_1")
+ }
+
+ @Test
+ fun umo_mediaPlaying_showsUmo() =
+ testScope.runTest {
+ // Tutorial completed.
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Media is playing.
+ mediaRepository.mediaPlaying.value = true
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ assertThat(communalContent?.size).isEqualTo(1)
+ assertThat(communalContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
+ assertThat(communalContent?.get(0)?.key).isEqualTo(CommunalContentModel.UMO_KEY)
+ }
+
+ @Test
+ fun contentOrdering() =
+ testScope.runTest {
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Widgets available.
+ val widgets =
+ listOf(
+ CommunalWidgetContentModel(
+ appWidgetId = 0,
+ priority = 30,
+ providerInfo = mock(),
+ ),
+ CommunalWidgetContentModel(
+ appWidgetId = 1,
+ priority = 20,
+ providerInfo = mock(),
+ ),
+ )
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // Smartspace available.
+ val target = mock(SmartspaceTarget::class.java)
+ whenever(target.smartspaceTargetId).thenReturn("target")
+ whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+ whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
+ smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+
+ // Media playing.
+ mediaRepository.mediaPlaying.value = true
+
+ val communalContent by collectLastValue(underTest.communalContent)
+
+ // Order is smart space, then UMO, then widget content.
+ assertThat(communalContent?.size).isEqualTo(4)
+ assertThat(communalContent?.get(0))
+ .isInstanceOf(CommunalContentModel.Smartspace::class.java)
+ assertThat(communalContent?.get(1)).isInstanceOf(CommunalContentModel.Umo::class.java)
+ assertThat(communalContent?.get(2))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
+ assertThat(communalContent?.get(3))
+ .isInstanceOf(CommunalContentModel.Widget::class.java)
}
@Test
fun listensToSceneChange() =
testScope.runTest {
- val interactor = CommunalInteractor(communalRepository, widgetRepository)
- var desiredScene = collectLastValue(interactor.desiredScene)
+ var desiredScene = collectLastValue(underTest.desiredScene)
runCurrent()
assertThat(desiredScene()).isEqualTo(CommunalSceneKey.Blank)
val targetScene = CommunalSceneKey.Communal
communalRepository.setDesiredScene(targetScene)
- desiredScene = collectLastValue(interactor.desiredScene)
+ desiredScene = collectLastValue(underTest.desiredScene)
runCurrent()
assertThat(desiredScene()).isEqualTo(targetScene)
}
@@ -106,10 +316,9 @@
@Test
fun updatesScene() =
testScope.runTest {
- val interactor = CommunalInteractor(communalRepository, widgetRepository)
val targetScene = CommunalSceneKey.Communal
- interactor.onSceneChanged(targetScene)
+ underTest.onSceneChanged(targetScene)
val desiredScene = collectLastValue(communalRepository.desiredScene)
runCurrent()
@@ -119,15 +328,13 @@
@Test
fun isCommunalShowing() =
testScope.runTest {
- val interactor = CommunalInteractor(communalRepository, widgetRepository)
-
- var isCommunalShowing = collectLastValue(interactor.isCommunalShowing)
+ var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(false)
- interactor.onSceneChanged(CommunalSceneKey.Communal)
+ underTest.onSceneChanged(CommunalSceneKey.Communal)
- isCommunalShowing = collectLastValue(interactor.isCommunalShowing)
+ isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
deleted file mode 100644
index 61d1502..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ /dev/null
@@ -1,317 +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.systemui.communal.domain.interactor
-
-import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
-import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
-import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-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.shared.model.CommunalSceneKey
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(JUnit4::class)
-class CommunalTutorialInteractorTest : SysuiTestCase() {
-
- @Mock private lateinit var userTracker: UserTracker
-
- private lateinit var testScope: TestScope
- private lateinit var underTest: CommunalTutorialInteractor
- private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var keyguardInteractor: KeyguardInteractor
- private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
- private lateinit var sceneContainerFlags: FakeSceneContainerFlags
- private lateinit var communalInteractor: CommunalInteractor
- private lateinit var communalRepository: FakeCommunalRepository
-
- private val utils = SceneTestUtils(this)
- private lateinit var sceneInteractor: SceneInteractor
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- sceneInteractor = utils.sceneInteractor()
- testScope = utils.testScope
- sceneContainerFlags = utils.sceneContainerFlags.apply { enabled = false }
- communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
- communalInteractor = CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
-
- val withDeps = KeyguardInteractorFactory.create()
- keyguardInteractor = withDeps.keyguardInteractor
- keyguardRepository = withDeps.repository
- communalTutorialRepository = FakeCommunalTutorialRepository()
-
- underTest =
- CommunalTutorialInteractor(
- scope = testScope.backgroundScope,
- communalTutorialRepository = communalTutorialRepository,
- keyguardInteractor = keyguardInteractor,
- communalInteractor = communalInteractor,
- sceneContainerFlags = sceneContainerFlags,
- sceneInteractor = sceneInteractor,
- )
-
- whenever(userTracker.userHandle).thenReturn(mock())
- }
-
- @Test
- fun tutorialUnavailable_whenKeyguardNotVisible() =
- testScope.runTest {
- val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- keyguardRepository.setKeyguardShowing(false)
- assertThat(isTutorialAvailable).isFalse()
- }
-
- @Test
- fun tutorialUnavailable_whenTutorialIsCompleted() =
- testScope.runTest {
- val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- assertThat(isTutorialAvailable).isFalse()
- }
-
- @Test
- fun tutorialAvailable_whenTutorialNotStarted() =
- testScope.runTest {
- val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- assertThat(isTutorialAvailable).isTrue()
- }
-
- @Test
- fun tutorialAvailable_whenTutorialIsStarted() =
- testScope.runTest {
- val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- assertThat(isTutorialAvailable).isTrue()
- }
-
- /* Testing tutorial states with transitions when flexiglass off */
- @Test
- fun tutorialState_notStartedAndCommunalSceneShowing_tutorialStarted() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(communalInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-
- assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
- }
-
- @Test
- fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(communalInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-
- assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
- }
-
- @Test
- fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(communalInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-
- assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
- }
-
- @Test
- fun tutorialState_notStartedAndCommunalSceneNotShowing_stateWillNotUpdate() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(communalInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-
- assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
- }
-
- @Test
- fun tutorialState_startedAndCommunalSceneNotShowing_tutorialCompleted() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(communalInteractor.desiredScene)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-
- assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
- }
-
- @Test
- fun tutorialState_completedAndCommunalSceneNotShowing_stateWillNotUpdate() =
- testScope.runTest {
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(communalInteractor.desiredScene)
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-
- assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
- }
-
- /* Testing tutorial states with transitions when flexiglass on */
- @Test
- fun tutorialState_notStartedCommunalSceneShowingAndFlexiglassOn_tutorialStarted() =
- testScope.runTest {
- sceneContainerFlags.enabled = true
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
- }
-
- @Test
- fun tutorialState_startedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
- testScope.runTest {
- sceneContainerFlags.enabled = true
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
- }
-
- @Test
- fun tutorialState_completedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
- testScope.runTest {
- sceneContainerFlags.enabled = true
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
- }
-
- @Test
- fun tutorialState_notStartedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
- testScope.runTest {
- sceneContainerFlags.enabled = true
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
- }
-
- @Test
- fun tutorialState_startedCommunalSceneNotShowingAndFlexiglassOn_tutorialCompleted() =
- testScope.runTest {
- sceneContainerFlags.enabled = true
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
- }
-
- @Test
- fun tutorialState_completedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
- testScope.runTest {
- sceneContainerFlags.enabled = true
- val tutorialSettingState by
- collectLastValue(communalTutorialRepository.tutorialSettingState)
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
- communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
- sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
- assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
index 40f0ed3..288f3b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -33,6 +33,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.reference.FakeWeakReferenceFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -68,7 +69,8 @@
mExecutor,
/* overlayEnabled= */ true,
mFeatureFlags,
- FakeLogBuffer.Factory.Companion.create());
+ FakeLogBuffer.Factory.Companion.create(),
+ new FakeWeakReferenceFactory());
mLiveData = new ComplicationCollectionLiveData(mStateController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index 0db3de2..1f18705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -63,7 +63,8 @@
ConnectedDisplayInteractorImpl(
virtualDeviceManager,
fakeKeyguardRepository,
- fakeDisplayRepository
+ fakeDisplayRepository,
+ UnconfinedTestDispatcher(),
)
private val testScope = TestScope(UnconfinedTestDispatcher())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
index dcc15ae..b25fb6e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
@@ -20,8 +20,8 @@
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import org.junit.After
@@ -45,7 +45,13 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- dialog = MirroringConfirmationDialog(context, onStartMirroringCallback, onCancelCallback)
+ dialog =
+ MirroringConfirmationDialog(
+ context,
+ onStartMirroringCallback,
+ onCancelCallback,
+ navbarBottomInsetsProvider = { 0 },
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 365f67b..6d5cd49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.reference.FakeWeakReferenceFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -63,6 +64,8 @@
final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ final FakeWeakReferenceFactory mWeakReferenceFactory = new FakeWeakReferenceFactory();
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -407,12 +410,36 @@
assertThat(stateController.getComplications()).contains(homeControlsComplication);
}
+ @Test
+ public void testCallbacksIgnoredWhenWeakReferenceCleared() {
+ final DreamOverlayStateController.Callback callback1 = Mockito.mock(
+ DreamOverlayStateController.Callback.class);
+ final DreamOverlayStateController.Callback callback2 = Mockito.mock(
+ DreamOverlayStateController.Callback.class);
+
+ final DreamOverlayStateController stateController = getDreamOverlayStateController(true);
+ stateController.addCallback(callback1);
+ stateController.addCallback(callback2);
+ mExecutor.runAllReady();
+
+ // Simulate callback1 getting GC'd by clearing the reference
+ mWeakReferenceFactory.clear(callback1);
+ stateController.setOverlayActive(true);
+ mExecutor.runAllReady();
+
+ // Callback2 should still be called, but never callback1
+ verify(callback1, never()).onStateChanged();
+ verify(callback2).onStateChanged();
+ assertThat(stateController.isOverlayActive()).isTrue();
+ }
+
private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) {
return new DreamOverlayStateController(
mExecutor,
overlayEnabled,
mFeatureFlags,
- mLogBuffer
+ mLogBuffer,
+ mWeakReferenceFactory
);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index b589a2a..a903d25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -22,6 +22,7 @@
import android.content.res.Resources
import android.content.res.Resources.NotFoundException
import android.test.suitebuilder.annotation.SmallTest
+import com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -72,6 +73,8 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ mSetFlagsRule.disableFlags(FLAG_SYSUI_TEAMFOOD)
+
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
mFeatureFlagsClassicDebug =
@@ -130,7 +133,7 @@
@Test
fun teamFoodFlag_True() {
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD)
+ mSetFlagsRule.enableFlags(FLAG_SYSUI_TEAMFOOD)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -145,7 +148,7 @@
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
.thenReturn(false)
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD)
+ mSetFlagsRule.enableFlags(FLAG_SYSUI_TEAMFOOD)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 814a317..b16c352 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -194,6 +194,7 @@
private @Captor ArgumentCaptor<KeyguardUpdateMonitorCallback>
mKeyguardUpdateMonitorCallbackCaptor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
+ private FakeExecutor mUiMainExecutor = new FakeExecutor(new FakeSystemClock());
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private FalsingCollectorFake mFalsingCollector;
@@ -247,6 +248,7 @@
mConfigurationController,
mViewMediator,
mKeyguardBypassController,
+ mUiMainExecutor,
mUiBgExecutor,
mColorExtractor,
mDumpManager,
@@ -255,7 +257,8 @@
mAuthController,
() -> mShadeInteractor,
mShadeWindowLogger,
- () -> mSelectedUserInteractor);
+ () -> mSelectedUserInteractor,
+ mUserTracker);
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
mFeatureFlags.set(Flags.REFACTOR_GETCURRENTUSER, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 90fd652..4587ea6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -154,6 +154,7 @@
fun fingerprintEnrollmentChange() =
testScope.runTest {
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
runCurrent()
@@ -170,11 +171,34 @@
}
@Test
+ fun fingerprintEnabledStateChange() =
+ testScope.runTest {
+ createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
+ val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
+ runCurrent()
+
+ // start state
+ whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true)
+ enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
+ assertThat(fingerprintAllowed()).isTrue()
+
+ // when biometrics are not enabled by settings
+ biometricsAreNotEnabledBySettings()
+ assertThat(fingerprintAllowed()).isFalse()
+
+ // when biometrics are enabled by settings
+ biometricsAreEnabledBySettings()
+ assertThat(fingerprintAllowed()).isTrue()
+ }
+
+ @Test
fun strongBiometricAllowedChange() =
testScope.runTest {
fingerprintIsEnrolled()
doNotDisableKeyguardAuthFeatures()
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val strongBiometricAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
@@ -197,7 +221,7 @@
createBiometricSettingsRepository()
val convenienceFaceAuthAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
doNotDisableKeyguardAuthFeatures()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(true, PRIMARY_USER_ID)
@@ -238,6 +262,7 @@
faceAuthIsNonStrongBiometric()
faceAuthIsEnrolled()
doNotDisableKeyguardAuthFeatures()
+ biometricsAreEnabledBySettings()
val convenienceBiometricAllowed = collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
runCurrent()
@@ -258,7 +283,7 @@
faceAuthIsEnrolled()
createBiometricSettingsRepository()
doNotDisableKeyguardAuthFeatures()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
runCurrent()
val convenienceBiometricAllowed by
@@ -291,6 +316,7 @@
testScope.runTest {
fingerprintIsEnrolled(PRIMARY_USER_ID)
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val fingerprintEnabledByDevicePolicy =
collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
@@ -316,7 +342,7 @@
createBiometricSettingsRepository()
val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
@@ -351,12 +377,18 @@
assertThat(faceAuthAllowed()).isTrue()
}
- private fun faceAuthIsEnabledByBiometricManager(userId: Int = PRIMARY_USER_ID) {
+ private fun biometricsAreEnabledBySettings(userId: Int = PRIMARY_USER_ID) {
verify(biometricManager, atLeastOnce())
.registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
biometricManagerCallback.value.onChanged(true, userId)
}
+ private fun biometricsAreNotEnabledBySettings(userId: Int = PRIMARY_USER_ID) {
+ verify(biometricManager, atLeastOnce())
+ .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+ biometricManagerCallback.value.onChanged(false, userId)
+ }
+
@Test
fun faceEnrollmentStatusOfNewUserUponUserSwitch() =
testScope.runTest {
@@ -427,7 +459,7 @@
faceAuthIsEnrolled()
createBiometricSettingsRepository()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
doNotDisableKeyguardAuthFeatures()
mobileConnectionsRepository.isAnySimSecure.value = false
runCurrent()
@@ -454,7 +486,7 @@
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
mobileConnectionsRepository.isAnySimSecure.value = false
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
@@ -636,7 +668,7 @@
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
@@ -660,7 +692,7 @@
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsNonStrongBiometric()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
@@ -682,6 +714,7 @@
fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() =
testScope.runTest {
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val isFingerprintCurrentlyAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
@@ -723,6 +756,7 @@
fun fpAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFpIsStrong() =
testScope.runTest {
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val isFingerprintCurrentlyAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
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 4f6ec71..b439fcf 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,23 +18,34 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.keyguard.util.mockTopActivityClassName
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import dagger.BindsInstance
+import dagger.Component
import dagger.Lazy
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import junit.framework.Assert.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -49,20 +60,30 @@
underTest
}
+ private lateinit var testComponent: TestComponent
+ @Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper
+
+ private var topActivityClassName = "launcher"
+
@Before
override fun setUp() {
super.setUp()
+ MockitoAnnotations.initMocks(this)
- underTest =
- FromLockscreenTransitionInteractor(
- transitionRepository = super.transitionRepository,
- transitionInteractor = super.transitionInteractor,
- scope = super.testScope.backgroundScope,
- keyguardInteractor = super.keyguardInteractor,
- flags = FakeFeatureFlags(),
- shadeRepository = FakeShadeRepository(),
- powerInteractor = PowerInteractorFactory.create().powerInteractor,
- )
+ testComponent =
+ DaggerFromLockscreenTransitionInteractorTest_TestComponent.factory()
+ .create(
+ test = this,
+ mocks =
+ TestMocksModule(
+ activityManagerWrapper = activityManagerWrapper,
+ ),
+ )
+ underTest = testComponent.underTest
+ testScope = testComponent.testScope
+ transitionRepository = testComponent.transitionRepository
+
+ activityManagerWrapper.mockTopActivityClassName(topActivityClassName)
}
@Test
@@ -189,4 +210,73 @@
fail("surfaceBehindModel was unexpectedly null.")
}
}
+
+ @Test
+ fun testSurfaceBehindModel_alpha1_whenTransitioningWithInWindowAnimation() =
+ testScope.runTest {
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ topActivityClassName
+ )
+ runCurrent()
+
+ val values by collectValues(underTest.surfaceBehindModel)
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(1f, values[values.size - 1]?.alpha)
+ }
+
+ @Test
+ fun testSurfaceBehindModel_alphaZero_whenNotTransitioningWithInWindowAnimation() =
+ testScope.runTest {
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ "not_launcher"
+ )
+ runCurrent()
+
+ val values by collectValues(underTest.surfaceBehindModel)
+ runCurrent()
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(0f, values[values.size - 1]?.alpha)
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ]
+ )
+ interface TestComponent {
+ val underTest: FromLockscreenTransitionInteractor
+ val testScope: TestScope
+ val transitionRepository: FakeKeyguardTransitionRepository
+ val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository
+ val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
}
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
new file mode 100644
index 0000000..7fb0dd5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -0,0 +1,454 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.mockTopActivityClassName
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import dagger.BindsInstance
+import dagger.Component
+import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() {
+ private lateinit var underTest: InWindowLauncherUnlockAnimationInteractor
+
+ private lateinit var testComponent: TestComponent
+ private lateinit var testScope: TestScope
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+ @Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper
+
+ private val launcherClassName = "launcher"
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testComponent =
+ DaggerInWindowLauncherUnlockAnimationInteractorTest_TestComponent.factory()
+ .create(
+ test = this,
+ mocks =
+ TestMocksModule(
+ activityManagerWrapper = activityManagerWrapper,
+ ),
+ )
+ underTest = testComponent.underTest
+ testScope = testComponent.testScope
+ transitionRepository = testComponent.transitionRepository
+
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ }
+
+ @Test
+ fun testTransitioningToGoneWithInWindowAnimation_trueIfTopActivityIsLauncher_andTransitioningToGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ // Put launcher on top
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ launcherClassName
+ )
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ runCurrent()
+
+ // Should still be false since we're not transitioning to GONE.
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true, // -> GONE + launcher is behind
+ ),
+ values
+ )
+
+ activityManagerWrapper.mockTopActivityClassName("not_launcher")
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true, // Top activity should be sampled, if it changes midway it should not
+ // matter.
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false, // False once we're not transitioning anymore.
+ ),
+ values
+ )
+ }
+
+ @Test
+ fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherPartwayThrough() =
+ testScope.runTest {
+ val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ // Put not launcher on top
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ launcherClassName
+ )
+ activityManagerWrapper.mockTopActivityClassName("not_launcher")
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+ }
+
+ @Test
+ fun testTransitioningToGoneWithInWindowAnimation_falseIfTopActivityIsLauncherWhileNotTransitioningToGone() =
+ testScope.runTest {
+ val values by collectValues(underTest.transitioningToGoneWithInWindowAnimation)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ // Put launcher on top
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ launcherClassName
+ )
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+ }
+
+ @Test
+ fun testShouldStartInWindowAnimation_trueOnceSurfaceAvailable_falseWhenTransitionEnds() =
+ testScope.runTest {
+ val values by collectValues(underTest.shouldStartInWindowAnimation)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ // Put Launcher on top and begin transitioning to GONE.
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ launcherClassName
+ )
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+
+ testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true, // The surface is now available, so we should start the animation.
+ ),
+ values
+ )
+
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ ),
+ values
+ )
+ }
+
+ @Test
+ fun testShouldStartInWindowAnimation_neverTrueIfSurfaceNotAvailable() =
+ testScope.runTest {
+ val values by collectValues(underTest.shouldStartInWindowAnimation)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ // Put Launcher on top and begin transitioning to GONE.
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ launcherClassName
+ )
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.FINISHED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+ }
+
+ @Test
+ fun testShouldStartInWindowAnimation_falseIfSurfaceAvailable_afterTransitionInterrupted() =
+ testScope.runTest {
+ val values by collectValues(underTest.shouldStartInWindowAnimation)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // False by default.
+ ),
+ values
+ )
+
+ // Put Launcher on top and begin transitioning to GONE.
+ testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ launcherClassName
+ )
+ activityManagerWrapper.mockTopActivityClassName(launcherClassName)
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ )
+ )
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ )
+ )
+ testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ ),
+ values
+ )
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ]
+ )
+ interface TestComponent {
+ val underTest: InWindowLauncherUnlockAnimationInteractor
+ val testScope: TestScope
+ val transitionRepository: FakeKeyguardTransitionRepository
+ val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository
+ val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ mocks: TestMocksModule,
+ ): TestComponent
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt
index 8db19ae..339fd22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTestCase.kt
@@ -26,7 +26,7 @@
open class KeyguardTransitionInteractorTestCase : SysuiTestCase() {
val testDispatcher = StandardTestDispatcher()
- val testScope = TestScope(testDispatcher)
+ var testScope = TestScope(testDispatcher)
lateinit var keyguardRepository: FakeKeyguardRepository
lateinit var transitionRepository: FakeKeyguardTransitionRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 275ac80..c292102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -27,7 +27,9 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
@@ -44,6 +46,7 @@
import com.android.systemui.shade.domain.model.ShadeModel
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
@@ -147,6 +150,15 @@
flags = featureFlags,
shadeRepository = shadeRepository,
powerInteractor = powerInteractor,
+ inWindowLauncherUnlockAnimationInteractor = {
+ InWindowLauncherUnlockAnimationInteractor(
+ InWindowLauncherUnlockAnimationRepository(),
+ testScope,
+ transitionInteractor,
+ { FakeKeyguardSurfaceBehindRepository() },
+ mock(),
+ )
+ },
)
.apply { start() }
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
new file mode 100644
index 0000000..570dfb3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -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.systemui.keyguard.ui.binder
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestModule
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.util.mockito.any
+import dagger.BindsInstance
+import dagger.Component
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class InWindowLauncherUnlockAnimationManagerTest : SysuiTestCase() {
+ private lateinit var underTest: InWindowLauncherUnlockAnimationManager
+
+ private lateinit var testComponent: TestComponent
+ private lateinit var testScope: TestScope
+
+ @Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testComponent =
+ DaggerInWindowLauncherUnlockAnimationManagerTest_TestComponent.factory()
+ .create(
+ test = this,
+ )
+ underTest = testComponent.underTest
+ testScope = testComponent.testScope
+
+ underTest.setLauncherUnlockController("launcherClass", launcherUnlockAnimationController)
+ }
+
+ @Test
+ fun testPrepareForUnlock_calledOnlyOnce() =
+ testScope.runTest {
+ underTest.prepareForUnlock()
+ underTest.prepareForUnlock()
+
+ verify(launcherUnlockAnimationController)
+ .prepareForUnlock(anyBoolean(), any(), anyInt())
+ }
+
+ @Test
+ fun testPlayUnlockAnimation_onlyCalledIfPrepared() =
+ testScope.runTest {
+ underTest.playUnlockAnimation(true, 200, 0)
+ verify(launcherUnlockAnimationController, never())
+ .playUnlockAnimation(any(), any(), any())
+ }
+
+ @Test
+ fun testForceUnlocked_ifPreparedButNeverStarted() =
+ testScope.runTest {
+ underTest.prepareForUnlock()
+ underTest.ensureUnlockedOrAnimatingUnlocked()
+
+ verify(launcherUnlockAnimationController).setUnlockAmount(1f, true)
+ }
+
+ @Test
+ fun testForceUnlocked_ifManualUnlockAmountLessThan1() =
+ testScope.runTest {
+ underTest.prepareForUnlock()
+ underTest.setUnlockAmount(0.5f, false)
+ underTest.ensureUnlockedOrAnimatingUnlocked()
+
+ verify(launcherUnlockAnimationController).prepareForUnlock(any(), any(), any())
+ verify(launcherUnlockAnimationController).setUnlockAmount(0.5f, false)
+ verify(launcherUnlockAnimationController).setUnlockAmount(1f, true)
+ verifyNoMoreInteractions(launcherUnlockAnimationController)
+ }
+
+ @Test
+ fun testDoesNotForceUnlocked_ifNeverPrepared() =
+ testScope.runTest {
+ underTest.ensureUnlockedOrAnimatingUnlocked()
+
+ verifyNoMoreInteractions(launcherUnlockAnimationController)
+ }
+
+ @SysUISingleton
+ @Component(
+ modules =
+ [
+ SysUITestModule::class,
+ ]
+ )
+ interface TestComponent {
+ val underTest: InWindowLauncherUnlockAnimationManager
+ val testScope: TestScope
+
+ @Component.Factory
+ interface Factory {
+ fun create(
+ @BindsInstance test: SysuiTestCase,
+ ): TestComponent
+ }
+ }
+}
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 c7f7c3c..71313c8 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
@@ -26,6 +26,7 @@
import com.android.keyguard.LockIconViewController
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
@@ -59,9 +60,11 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+
featureFlags =
FakeFeatureFlagsClassic().apply {
- set(Flags.MIGRATE_LOCK_ICON, false)
set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
}
@@ -81,7 +84,7 @@
@Test
fun addViewsConditionally_migrateFlagOn() {
- featureFlags.set(Flags.MIGRATE_LOCK_ICON, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -89,7 +92,7 @@
@Test
fun addViewsConditionally_migrateAndRefactorFlagsOn() {
- featureFlags.set(Flags.MIGRATE_LOCK_ICON, true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
@@ -98,7 +101,7 @@
@Test
fun addViewsConditionally_migrateFlagOff() {
- featureFlags.set(Flags.MIGRATE_LOCK_ICON, false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
featureFlags.set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, false)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 8b8c59b..8dd33d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -23,12 +23,10 @@
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.statusbar.KeyguardIndicationController
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -43,7 +41,6 @@
@Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
@Mock private lateinit var keyguardRootViewModel: KeyguardRootViewModel
@Mock private lateinit var indicationController: KeyguardIndicationController
- @Mock private lateinit var featureFlags: FeatureFlags
private lateinit var underTest: DefaultIndicationAreaSection
@@ -56,13 +53,12 @@
keyguardIndicationAreaViewModel,
keyguardRootViewModel,
indicationController,
- featureFlags,
)
}
@Test
fun addViewsConditionally() {
- whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(true)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isGreaterThan(0)
@@ -70,7 +66,7 @@
@Test
fun addViewsConditionally_migrateFlagOff() {
- whenever(featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)).thenReturn(false)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
underTest.addViews(constraintLayout)
assertThat(constraintLayout.childCount).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
index 34d93fc..88a4aa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -20,7 +20,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.util.BurnInHelperWrapper
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
@@ -29,7 +28,6 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -40,14 +38,12 @@
import org.mockito.Mock
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class KeyguardIndicationAreaViewModelTest : SysuiTestCase() {
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel
- @Mock private lateinit var featureFlags: FeatureFlags
private lateinit var underTest: KeyguardIndicationAreaViewModel
private lateinit var repository: FakeKeyguardRepository
@@ -87,7 +83,6 @@
keyguardBottomAreaViewModel = bottomAreaViewModel,
burnInHelperWrapper = burnInHelperWrapper,
shortcutsCombinedViewModel = shortcutsCombinedViewModel,
- featureFlags = featureFlags,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 1c6cc87..25d1419 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dock.DockManagerFake
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
@@ -123,9 +124,11 @@
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
dockManager = DockManagerFake()
biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+
val featureFlags =
FakeFeatureFlags().apply {
- set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true)
set(Flags.FACE_AUTH_REFACTOR, true)
set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false)
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 985b6fd..259c74f 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
@@ -32,6 +32,7 @@
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
@@ -106,9 +107,10 @@
testScope = TestScope(testDispatcher)
MockitoAnnotations.initMocks(this)
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
+
val featureFlags =
FakeFeatureFlagsClassic().apply {
- set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true)
set(Flags.FACE_AUTH_REFACTOR, true)
}
@@ -351,7 +353,6 @@
featureFlags =
FakeFeatureFlagsClassicModule {
setDefault(Flags.NEW_AOD_TRANSITION)
- set(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA, true)
set(Flags.FACE_AUTH_REFACTOR, true)
},
mocks =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/ActivityManagerWrapperMock.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/ActivityManagerWrapperMock.kt
new file mode 100644
index 0000000..2cb7e65
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/ActivityManagerWrapperMock.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.keyguard.util
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+/**
+ * Configures an ActivityManagerWrapper mock to return the given class name whenever we ask for the
+ * running task's top activity class name.
+ */
+fun ActivityManagerWrapper.mockTopActivityClassName(name: String) {
+ val topActivityMock = mock<ComponentName>().apply { whenever(className).thenReturn(name) }
+
+ whenever(runningTask)
+ .thenReturn(ActivityManager.RunningTaskInfo().apply { topActivity = topActivityMock })
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index 7ad2ce8..f4293f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -24,6 +24,7 @@
import android.view.View.VISIBLE
import android.widget.FrameLayout
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -32,6 +33,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.util.animation.UniqueObjectHostView
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
@@ -91,7 +93,8 @@
settings,
fakeHandler,
configurationController,
- ResourcesSplitShadeStateController()
+ ResourcesSplitShadeStateController(),
+ mock<DumpManager>()
)
keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
keyguardMediaController.useSplitShade = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 5bfe569..a2eb5ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -26,8 +26,7 @@
import com.android.keyguard.KeyguardViewController
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
@@ -106,7 +105,7 @@
private val configurationController = FakeConfigurationController()
private val communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
private val communalInteractor =
- CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
+ CommunalInteractorFactory.create(communalRepository = communalRepository).communalInteractor
private val notifPanelEvents = ShadeExpansionStateManager()
private val settings = FakeSettings()
private lateinit var testableLooper: TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index c835146..8a531fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -111,7 +111,6 @@
TaskStackChangeListeners.getTestInstance(),
Optional.of(mock(Pip.class)),
Optional.of(mock(BackAnimation.class)),
- mock(FeatureFlags.class),
mock(SecureSettings.class),
mDisplayTracker));
initializeNavigationBars();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 48a36cb..ddceed6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -27,6 +27,7 @@
import static android.view.WindowInsets.Type.ime;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
+import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -42,6 +43,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -86,6 +88,7 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.DeadZone;
+import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
@@ -120,6 +123,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -143,6 +147,8 @@
@Mock
ButtonDispatcher mHomeButton;
@Mock
+ KeyButtonView mHomeButtonView;
+ @Mock
ButtonDispatcher mRecentsButton;
@Mock
ButtonDispatcher mAccessibilityButton;
@@ -294,11 +300,38 @@
@Test
public void testHomeLongPress() {
+ when(mAssistManager.shouldOverrideAssist(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS))
+ .thenReturn(false);
+
mNavigationBar.init();
mNavigationBar.onViewAttached();
- mNavigationBar.onHomeLongClick(mNavigationBar.getView());
+ mNavigationBar.onHomeLongClick(mHomeButtonView);
verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
+ verify(mAssistManager).startAssist(any());
+ }
+
+ @Test
+ public void testHomeLongPressOverride() {
+ when(mAssistManager.shouldOverrideAssist(INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS))
+ .thenReturn(true);
+
+ mNavigationBar.init();
+ mNavigationBar.onViewAttached();
+ mNavigationBar.onHomeLongClick(mHomeButtonView);
+
+ verify(mUiEventLogger, times(1)).log(NAVBAR_ASSIST_LONGPRESS);
+
+ ArgumentCaptor<Runnable> onRippleInvisibleRunnableCaptor = ArgumentCaptor.forClass(
+ Runnable.class);
+ // startAssist is not called initially
+ verify(mAssistManager, never()).startAssist(any());
+ // but a Runnable is added for when the ripple is invisible
+ verify(mHomeButtonView).setOnRippleInvisibleRunnable(
+ onRippleInvisibleRunnableCaptor.capture());
+ // and when that runs, startAssist is called
+ onRippleInvisibleRunnableCaptor.getValue().run();
+ verify(mAssistManager).startAssist(any());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index 078a917..a1010a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -50,6 +50,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
import com.android.systemui.recents.OverviewProxyService;
import org.junit.Before;
@@ -76,6 +77,7 @@
MockitoAnnotations.initMocks(this);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(AssistManager.class);
mUiEventLogger = mDependency.injectMockDependency(UiEventLogger.class);
TestableLooper.get(this).runWithLooper(() -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index 2d3dc58..f93d52b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -30,8 +30,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
import com.android.systemui.plugins.NavigationEdgeBackPlugin
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -63,7 +61,6 @@
@Mock private lateinit var latencyTracker: LatencyTracker
@Mock private lateinit var layoutParams: WindowManager.LayoutParams
@Mock private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
- private val featureFlags = FakeFeatureFlags()
@Before
fun setup() {
@@ -77,7 +74,6 @@
vibratorHelper,
configurationController,
latencyTracker,
- featureFlags
)
mBackPanelController.setLayoutParams(layoutParams)
mBackPanelController.setBackCallback(backCallback)
@@ -106,32 +102,6 @@
@Test
fun handlesBackCommitted() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
- startTouch()
- // Move once to cross the touch slop
- continueTouch(START_X + touchSlop.toFloat() + 1)
- // Move again to cross the back trigger threshold
- continueTouch(START_X + touchSlop + triggerThreshold + 1)
- // Wait threshold duration and hold touch past trigger threshold
- Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
- continueTouch(START_X + touchSlop + triggerThreshold + 1)
-
- assertThat(mBackPanelController.currentState)
- .isEqualTo(BackPanelController.GestureState.ACTIVE)
- verify(backCallback).setTriggerBack(true)
- testableLooper.moveTimeForward(100)
- testableLooper.processAllMessages()
- verify(vibratorHelper).vibrate(VIBRATE_ACTIVATED_EFFECT)
-
- finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
- assertThat(mBackPanelController.currentState)
- .isEqualTo(BackPanelController.GestureState.COMMITTED)
- verify(backCallback).triggerBack()
- }
-
- @Test
- fun handlesBackCommitted_withOneWayHapticsAPI() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
startTouch()
// Move once to cross the touch slop
continueTouch(START_X + touchSlop.toFloat() + 1)
@@ -148,7 +118,6 @@
testableLooper.processAllMessages()
verify(vibratorHelper)
.performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE))
-
finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
assertThat(mBackPanelController.currentState)
.isEqualTo(BackPanelController.GestureState.COMMITTED)
@@ -157,38 +126,6 @@
@Test
fun handlesBackCancelled() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
- startTouch()
- // Move once to cross the touch slop
- continueTouch(START_X + touchSlop.toFloat() + 1)
- // Move again to cross the back trigger threshold
- continueTouch(
- START_X + touchSlop + triggerThreshold -
- mBackPanelController.params.deactivationTriggerThreshold
- )
- // Wait threshold duration and hold touch before trigger threshold
- Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
- continueTouch(
- START_X + touchSlop + triggerThreshold -
- mBackPanelController.params.deactivationTriggerThreshold
- )
- clearInvocations(backCallback)
- Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
- // Move in the opposite direction to cross the deactivation threshold and cancel back
- continueTouch(START_X)
-
- assertThat(mBackPanelController.currentState)
- .isEqualTo(BackPanelController.GestureState.INACTIVE)
- verify(backCallback).setTriggerBack(false)
- verify(vibratorHelper).vibrate(VIBRATE_DEACTIVATED_EFFECT)
-
- finishTouchActionUp(START_X)
- verify(backCallback).cancelBack()
- }
-
- @Test
- fun handlesBackCancelled_withOneWayHapticsAPI() {
- featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
startTouch()
// Move once to cross the touch slop
continueTouch(START_X + touchSlop.toFloat() + 1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
index f566efe6..f3b114d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -210,6 +210,22 @@
assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_INDIRECT)
}
+ @Test
+ fun userActivity_notifiesPowerManager_noChangeLightsTrue() {
+ systemClock.setUptimeMillis(345000)
+
+ underTest.userTouch(noChangeLights = true)
+
+ val flagsCaptor = argumentCaptor<Int>()
+ verify(manager)
+ .userActivity(
+ eq(345000L),
+ eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
+ capture(flagsCaptor)
+ )
+ assertThat(flagsCaptor.value).isEqualTo(PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS)
+ }
+
private fun verifyRegistered() {
// We must verify with all arguments, even those that are optional because they have default
// values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index 31d02ed..8f27e4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -57,7 +57,7 @@
whenever(logBufferFactory.create(any(), any(), any())).thenReturn(logBuffer)
underTest =
QSTileLogger(
- mapOf(TileSpec.create("chatty_tile") to chattyLogBuffer),
+ mapOf("chatty_tile" to chattyLogBuffer),
logBufferFactory,
statusBarController
)
@@ -117,7 +117,7 @@
underTest.logUserActionPipeline(
TileSpec.create("test_spec"),
QSTileUserAction.Click(null),
- QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {},
+ QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {},
"test_data",
)
@@ -143,7 +143,7 @@
fun testLogStateUpdate() {
underTest.logStateUpdate(
TileSpec.create("test_spec"),
- QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {},
+ QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {},
"test_data",
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 9bf4a75..d3b7daa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -97,7 +97,10 @@
{
object : QSTileDataToStateMapper<Any> {
override fun map(config: QSTileConfig, data: Any): QSTileState =
- QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
+ QSTileState.build(
+ { Icon.Resource(0, ContentDescription.Resource(0)) },
+ ""
+ ) {}
}
},
fakeDisabledByPolicyInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 6d358db..70a48f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -30,8 +30,10 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
import com.android.systemui.model.SysUiState
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.navigationbar.NavigationModeController
@@ -100,6 +102,9 @@
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController
+ @Mock
+ private lateinit var inWindowLauncherUnlockAnimationManager:
+ InWindowLauncherUnlockAnimationManager
@Mock private lateinit var assistUtils: AssistUtils
@Mock
private lateinit var unfoldTransitionProgressForwarder:
@@ -126,6 +131,7 @@
whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
.thenReturn(mock(ResolveInfo::class.java))
+ featureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false)
subject =
OverviewProxyService(
context,
@@ -144,6 +150,7 @@
uiEventLogger,
displayTracker,
sysuiUnlockAnimationController,
+ inWindowLauncherUnlockAnimationManager,
assistUtils,
featureFlags,
FakeSceneContainerFlags(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 3feb5bf..e84d274 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -18,17 +18,23 @@
package com.android.systemui.scene
+import android.telecom.TelecomManager
+import android.telephony.TelephonyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
+import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.model.SysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -49,7 +55,9 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -63,6 +71,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
/**
* Integration test cases for the Scene Framework.
@@ -87,6 +99,10 @@
@RunWith(AndroidJUnit4::class)
class SceneFrameworkIntegrationTest : SysuiTestCase() {
+ @Mock private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
+ @Mock private lateinit var tableLogger: TableLogBuffer
+ @Mock private lateinit var telecomManager: TelecomManager
+
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneContainerConfig = utils.fakeSceneContainerConfig()
@@ -123,11 +139,10 @@
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
)
- private val bouncerViewModel =
- utils.bouncerViewModel(
- bouncerInteractor = bouncerInteractor,
- authenticationInteractor = authenticationInteractor,
- )
+
+ private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
+ private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
+ private lateinit var bouncerViewModel: BouncerViewModel
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
@@ -141,7 +156,6 @@
)
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private var mobileIconsViewModel: MobileIconsViewModel =
MobileIconsViewModel(
@@ -155,7 +169,7 @@
FakeMobileConnectionsRepository(),
),
constants = mock(),
- flags,
+ utils.featureFlags,
scope = testScope.backgroundScope,
)
@@ -173,6 +187,39 @@
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
+ whenever(telecomManager.isInCall).thenReturn(false)
+ whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
+
+ utils.featureFlags.apply {
+ set(Flags.NEW_NETWORK_SLICE_UI, false)
+ set(Flags.REFACTOR_GETCURRENTUSER, true)
+ }
+
+ mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
+ mobileConnectionsRepository.isAnySimSecure.value = true
+
+ utils.telephonyRepository.apply {
+ setHasTelephonyRadio(true)
+ setCallState(TelephonyManager.CALL_STATE_IDLE)
+ setIsInCall(false)
+ }
+
+ bouncerActionButtonInteractor =
+ utils.bouncerActionButtonInteractor(
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ telecomManager = telecomManager,
+ emergencyAffordanceManager = emergencyAffordanceManager,
+ )
+ bouncerViewModel =
+ utils.bouncerViewModel(
+ bouncerInteractor = bouncerInteractor,
+ authenticationInteractor = authenticationInteractor,
+ actionButtonInteractor = bouncerActionButtonInteractor,
+ )
+
shadeHeaderViewModel =
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
@@ -395,6 +442,45 @@
emulateUiSceneTransition()
}
+ @Test
+ fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
+ testScope.runTest {
+ setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ val upDestinationSceneKey by
+ collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
+ assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
+
+ val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
+ assertWithMessage("Bouncer action button not visible")
+ .that(bouncerActionButton)
+ .isNotNull()
+ bouncerActionButton?.onClick?.invoke()
+ runCurrent()
+
+ // TODO(b/298026988): Assert that an activity was started once we use ActivityStarter.
+ }
+
+ @Test
+ fun bouncerActionButtonClick_duringCall_returnsToCall() =
+ testScope.runTest {
+ setAuthMethod(DomainLayerAuthenticationMethodModel.Password)
+ startPhoneCall()
+ val upDestinationSceneKey by
+ collectLastValue(lockscreenSceneViewModel.upDestinationSceneKey)
+ assertThat(upDestinationSceneKey).isEqualTo(SceneKey.Bouncer)
+ emulateUserDrivenTransition(to = upDestinationSceneKey)
+
+ val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
+ assertWithMessage("Bouncer action button not visible during call")
+ .that(bouncerActionButton)
+ .isNotNull()
+ bouncerActionButton?.onClick?.invoke()
+ runCurrent()
+
+ verify(telecomManager).showInCallScreen(any())
+ }
+
/**
* Asserts that the current scene in the view-model matches what's expected.
*
@@ -438,6 +524,17 @@
runCurrent()
}
+ /** Emulates a phone call in progress. */
+ private fun TestScope.startPhoneCall() {
+ whenever(telecomManager.isInCall).thenReturn(true)
+ utils.telephonyRepository.apply {
+ setHasTelephonyRadio(true)
+ setIsInCall(true)
+ setCallState(TelephonyManager.CALL_STATE_OFFHOOK)
+ }
+ runCurrent()
+ }
+
/**
* Emulates a complete transition in the UI from whatever the current scene is in the UI to
* whatever the current scene should be, based on the value in
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 32a38bd..4c8b562 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -67,6 +67,7 @@
listOf(
AconfigFlags.FLAG_SCENE_CONTAINER,
+ AconfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
)
.forEach { flagToken ->
setFlagsRule.enableFlags(flagToken)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 49049bc..1b4ba64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -36,20 +36,27 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
import com.android.systemui.mediaprojection.SessionCreationSource;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.DialogDelegate;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -92,6 +99,7 @@
private FakeFeatureFlags mFeatureFlags;
private RecordingController mController;
+ private TestSystemUIDialogFactory mDialogFactory;
private static final int USER_ID = 10;
@@ -103,6 +111,15 @@
when(mUserContextProvider.getUserContext()).thenReturn(spiedContext);
+ mDialogFactory = new TestSystemUIDialogFactory(
+ mContext,
+ mFeatureFlags,
+ Dependency.get(SystemUIDialogManager.class),
+ Dependency.get(SysUiState.class),
+ Dependency.get(BroadcastDispatcher.class),
+ Dependency.get(DialogLaunchAnimator.class)
+ );
+
mFeatureFlags = new FakeFeatureFlags();
mController = new RecordingController(
mMainExecutor,
@@ -112,7 +129,8 @@
mUserContextProvider,
() -> mDevicePolicyResolver,
mUserTracker,
- mMediaProjectionMetricsLogger);
+ mMediaProjectionMetricsLogger,
+ mDialogFactory);
mController.addCallback(mCallback);
}
@@ -218,10 +236,17 @@
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
- Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
- mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+ Dialog dialog =
+ mController.createScreenRecordDialog(
+ mContext,
+ mFeatureFlags,
+ mDialogLaunchAnimator,
+ mActivityStarter,
+ /* onStartRecordingClicked= */ null);
- assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
+ assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog);
+ assertThat(mDialogFactory.mLastDelegate)
+ .isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
}
@Test
@@ -253,10 +278,17 @@
mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
- Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
- mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+ Dialog dialog =
+ mController.createScreenRecordDialog(
+ mContext,
+ mFeatureFlags,
+ mDialogLaunchAnimator,
+ mActivityStarter,
+ /* onStartRecordingClicked= */ null);
- assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
+ assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog);
+ assertThat(mDialogFactory.mLastDelegate)
+ .isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
}
@Test
@@ -273,4 +305,34 @@
/* hostUid= */ myUid(),
SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
}
+
+ private static class TestSystemUIDialogFactory extends SystemUIDialog.Factory {
+
+ @Nullable private DialogDelegate<SystemUIDialog> mLastDelegate;
+ @Nullable private SystemUIDialog mLastCreatedDialog;
+
+ TestSystemUIDialogFactory(
+ Context context,
+ FeatureFlags featureFlags,
+ SystemUIDialogManager systemUIDialogManager,
+ SysUiState sysUiState,
+ BroadcastDispatcher broadcastDispatcher,
+ DialogLaunchAnimator dialogLaunchAnimator) {
+ super(
+ context,
+ featureFlags,
+ systemUIDialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator);
+ }
+
+ @Override
+ public SystemUIDialog create(DialogDelegate<SystemUIDialog> delegate) {
+ SystemUIDialog dialog = super.create(delegate);
+ mLastDelegate = delegate;
+ mLastCreatedDialog = dialog;
+ return dialog;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
similarity index 80%
rename from packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index fd38139..c848287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -23,16 +23,22 @@
import android.view.View
import android.widget.Spinner
import androidx.test.filters.SmallTest
+import com.android.systemui.Dependency
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
import com.android.systemui.mediaprojection.permission.SINGLE_APP
+import com.android.systemui.model.SysUiState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -50,7 +56,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-class ScreenRecordPermissionDialogTest : SysuiTestCase() {
+class ScreenRecordPermissionDialogDelegateTest : SysuiTestCase() {
@Mock private lateinit var starter: ActivityStarter
@Mock private lateinit var controller: RecordingController
@@ -59,15 +65,23 @@
@Mock private lateinit var onStartRecordingClicked: Runnable
@Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
- private lateinit var dialog: ScreenRecordPermissionDialog
+ private lateinit var dialog: SystemUIDialog
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- dialog =
- ScreenRecordPermissionDialog(
+ val systemUIDialogFactory =
+ SystemUIDialog.Factory(
context,
+ Dependency.get(FeatureFlags::class.java),
+ Dependency.get(SystemUIDialogManager::class.java),
+ Dependency.get(SysUiState::class.java),
+ Dependency.get(BroadcastDispatcher::class.java),
+ Dependency.get(DialogLaunchAnimator::class.java),
+ )
+ val delegate =
+ ScreenRecordPermissionDialogDelegate(
UserHandle.of(0),
TEST_HOST_UID,
controller,
@@ -76,20 +90,21 @@
onStartRecordingClicked,
mediaProjectionMetricsLogger,
)
- dialog.onCreate(null)
+ dialog = systemUIDialogFactory.create(delegate)
+ delegate.onCreate(dialog, savedInstanceState = null)
whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
}
@After
fun teardown() {
if (::dialog.isInitialized) {
- dialog.dismiss()
+ dismissDialog()
}
}
@Test
fun testShowDialog_partialScreenSharingEnabled_optionsSpinnerIsVisible() {
- dialog.show()
+ showDialog()
val visibility = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner).visibility
assertThat(visibility).isEqualTo(View.VISIBLE)
@@ -97,7 +112,7 @@
@Test
fun testShowDialog_singleAppSelected_showTapsIsGone() {
- dialog.show()
+ showDialog()
onSpinnerItemSelected(SINGLE_APP)
val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
@@ -106,7 +121,7 @@
@Test
fun testShowDialog_entireScreenSelected_showTapsIsVisible() {
- dialog.show()
+ showDialog()
onSpinnerItemSelected(ENTIRE_SCREEN)
val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
@@ -115,7 +130,7 @@
@Test
fun startClicked_singleAppSelected_passesHostUidToAppSelector() {
- dialog.show()
+ showDialog()
onSpinnerItemSelected(SINGLE_APP)
clickOnStart()
@@ -128,14 +143,14 @@
@Test
fun showDialog_dialogIsShowing() {
- dialog.show()
+ showDialog()
assertThat(dialog.isShowing).isTrue()
}
@Test
fun showDialog_singleAppIsDefault() {
- dialog.show()
+ showDialog()
val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
val singleApp = context.getString(R.string.screen_share_permission_dialog_option_single_app)
@@ -144,7 +159,7 @@
@Test
fun showDialog_cancelClicked_dialogIsDismissed() {
- dialog.show()
+ showDialog()
clickOnCancel()
@@ -153,7 +168,7 @@
@Test
fun showDialog_cancelClickedMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
- dialog.show()
+ showDialog()
clickOnCancel()
clickOnCancel()
@@ -163,16 +178,22 @@
@Test
fun dismissDialog_dismissCalledMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
- dialog.show()
+ showDialog()
- TestableLooper.get(this).runWithLooper {
- dialog.dismiss()
- dialog.dismiss()
- }
+ dismissDialog()
+ dismissDialog()
verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
}
+ private fun showDialog() {
+ dialog.show()
+ }
+
+ private fun dismissDialog() {
+ dialog.dismiss()
+ }
+
private fun clickOnCancel() {
dialog.requireViewById<View>(android.R.id.button2).performClick()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 55a44ae..e6cd17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -59,9 +59,12 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository;
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository;
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -74,8 +77,10 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor;
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
import com.android.systemui.scene.shared.logger.SceneLogger;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
@@ -132,8 +137,10 @@
@Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Mock private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock private UserTracker mUserTracker;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
+ private final Executor mMainExecutor = MoreExecutors.directExecutor();
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
private SceneTestUtils mUtils = new SceneTestUtils(this);
private TestScope mTestScope = mUtils.getTestScope();
@@ -207,7 +214,16 @@
keyguardInteractor,
featureFlags,
shadeRepository,
- powerInteractor);
+ powerInteractor,
+ () ->
+ new InWindowLauncherUnlockAnimationInteractor(
+ new InWindowLauncherUnlockAnimationRepository(),
+ mTestScope.getBackgroundScope(),
+ keyguardTransitionInteractor,
+ () -> new FakeKeyguardSurfaceBehindRepository(),
+ mock(ActivityManagerWrapper.class)
+ )
+ );
mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
keyguardTransitionRepository,
@@ -248,6 +264,7 @@
mConfigurationController,
mKeyguardViewMediator,
mKeyguardBypassController,
+ mMainExecutor,
mBackgroundExecutor,
mColorExtractor,
mDumpManager,
@@ -256,7 +273,8 @@
mAuthController,
() -> mShadeInteractor,
mShadeWindowLogger,
- () -> mSelectedUserInteractor) {
+ () -> mSelectedUserInteractor,
+ mUserTracker) {
@Override
protected boolean isDebuggable() {
return false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index d4b35a9..6d04887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -45,9 +45,12 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository;
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository;
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
@@ -68,6 +71,7 @@
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -241,7 +245,16 @@
keyguardInteractor,
featureFlags,
mShadeRepository,
- powerInteractor);
+ powerInteractor,
+ () ->
+ new InWindowLauncherUnlockAnimationInteractor(
+ new InWindowLauncherUnlockAnimationRepository(),
+ mTestScope.getBackgroundScope(),
+ keyguardTransitionInteractor,
+ () -> new FakeKeyguardSurfaceBehindRepository(),
+ mock(ActivityManagerWrapper.class)
+ )
+ );
mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
keyguardTransitionRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index d6dfc5e..4b79a49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -29,9 +29,12 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -131,7 +134,16 @@
keyguardInteractor,
featureFlags,
shadeRepository,
- powerInteractor)
+ powerInteractor,
+ {
+ InWindowLauncherUnlockAnimationInteractor(
+ InWindowLauncherUnlockAnimationRepository(),
+ testScope,
+ keyguardTransitionInteractor,
+ { FakeKeyguardSurfaceBehindRepository() },
+ mock(),
+ )
+ })
fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
keyguardTransitionRepository,
keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 74b2723..cce038f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -94,6 +94,7 @@
override val defaultMobileIconGroup = _defaultMobileIconGroup
override val isAnySimSecure = MutableStateFlow(false)
+ override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value
private var isInEcmMode: Boolean = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index ac75d4f..03f3005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -92,7 +92,6 @@
// to run the callback and this makes the looper place nicely with TestScope etc.
@TestableLooper.RunWithLooper
class MobileConnectionsRepositoryTest : SysuiTestCase() {
- private lateinit var underTest: MobileConnectionsRepositoryImpl
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
@@ -101,6 +100,7 @@
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
private lateinit var wifiRepository: WifiRepository
private lateinit var carrierConfigRepository: CarrierConfigRepository
+
@Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var subscriptionManager: SubscriptionManager
@Mock private lateinit var telephonyManager: TelephonyManager
@@ -115,6 +115,8 @@
private val dispatcher = StandardTestDispatcher()
private val testScope = TestScope(dispatcher)
+ private lateinit var underTest: MobileConnectionsRepositoryImpl
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -1179,6 +1181,16 @@
}
@Test
+ fun getIsAnySimSecure_delegatesCallToKeyguardUpdateMonitor() =
+ testScope.runTest {
+ assertThat(underTest.getIsAnySimSecure()).isFalse()
+
+ whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+
+ assertThat(underTest.getIsAnySimSecure()).isTrue()
+ }
+
+ @Test
fun noSubscriptionsInEcmMode_notInEcmMode() =
testScope.runTest {
whenever(telephonyManager.emergencyCallbackMode).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 58d93c9..2f79955 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -294,6 +294,6 @@
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
when(mUsbPortStatus.getComplianceWarnings())
- .thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_OTHER});
+ .thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY});
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
deleted file mode 100644
index 0209030..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2022 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.telephony.data.repository
-
-import android.telephony.TelephonyCallback
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.telephony.TelephonyListenerManager
-import com.android.systemui.util.mockito.kotlinArgumentCaptor
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(JUnit4::class)
-class TelephonyRepositoryImplTest : SysuiTestCase() {
-
- @Mock private lateinit var manager: TelephonyListenerManager
-
- private lateinit var underTest: TelephonyRepositoryImpl
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- underTest =
- TelephonyRepositoryImpl(
- applicationContext = context,
- manager = manager,
- )
- }
-
- @Test
- fun callState() =
- runBlocking(IMMEDIATE) {
- var callState: Int? = null
- val job = underTest.callState.onEach { callState = it }.launchIn(this)
- val listenerCaptor = kotlinArgumentCaptor<TelephonyCallback.CallStateListener>()
- verify(manager).addCallStateListener(listenerCaptor.capture())
- val listener = listenerCaptor.value
-
- listener.onCallStateChanged(0)
- assertThat(callState).isEqualTo(0)
-
- listener.onCallStateChanged(1)
- assertThat(callState).isEqualTo(1)
-
- listener.onCallStateChanged(2)
- assertThat(callState).isEqualTo(2)
-
- job.cancel()
-
- verify(manager).removeCallStateListener(listener)
- }
-
- companion object {
- private val IMMEDIATE = Dispatchers.Main.immediate
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 1968d75..017eefe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -38,16 +38,14 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.user.UserSwitchDialogController
import com.android.systemui.res.R
+import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.source.UserRecord
@@ -65,9 +63,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -102,18 +97,16 @@
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- private lateinit var underTest: UserSwitcherInteractor
-
+ private val utils = SceneTestUtils(this)
+ private val testScope = utils.testScope
private lateinit var spyContext: Context
- private lateinit var testScope: TestScope
private lateinit var userRepository: FakeUserRepository
private lateinit var keyguardReply: KeyguardInteractorFactory.WithDependencies
private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var telephonyRepository: FakeTelephonyRepository
- private lateinit var testDispatcher: TestDispatcher
- private lateinit var featureFlags: FakeFeatureFlags
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
+ private lateinit var underTest: UserSwitcherInteractor
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -127,22 +120,17 @@
SUPERVISED_USER_CREATION_APP_PACKAGE,
)
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- set(Flags.FACE_AUTH_REFACTOR, true)
- }
+ utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ utils.featureFlags.set(Flags.FACE_AUTH_REFACTOR, true)
+
spyContext = spy(context)
- keyguardReply = KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.featureFlags)
keyguardRepository = keyguardReply.repository
userRepository = FakeUserRepository()
- telephonyRepository = FakeTelephonyRepository()
- testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
- mainDispatcher = testDispatcher,
+ mainDispatcher = utils.testDispatcher,
repository = userRepository,
)
}
@@ -372,7 +360,7 @@
fun actions_deviceUnlocked_fullScreen() {
createUserInteractor()
testScope.runTest {
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
@@ -456,7 +444,7 @@
fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() {
createUserInteractor()
testScope.runTest {
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 2, includeGuest = false)
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
@@ -649,7 +637,7 @@
val refreshUsersCallCount = userRepository.refreshUsersCallCount
- telephonyRepository.setCallState(1)
+ utils.telephonyRepository.setCallState(1)
runCurrent()
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
@@ -801,7 +789,7 @@
fun userRecordsFullScreen() {
createUserInteractor()
testScope.runTest {
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val userInfos = createUserInfos(count = 3, includeGuest = false)
userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
userRepository.setUserInfos(userInfos)
@@ -910,7 +898,7 @@
fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() {
createUserInteractor()
testScope.runTest {
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+ utils.featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val expandable = mock<Expandable>()
underTest.showUserSwitcher(expandable)
@@ -1125,21 +1113,18 @@
manager = manager,
headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
- telephonyInteractor =
- TelephonyInteractor(
- repository = telephonyRepository,
- ),
+ telephonyInteractor = utils.telephonyInteractor(),
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
- backgroundDispatcher = testDispatcher,
+ backgroundDispatcher = utils.testDispatcher,
activityManager = activityManager,
refreshUsersScheduler = refreshUsersScheduler,
guestUserInteractor =
GuestUserInteractor(
applicationContext = spyContext,
applicationScope = testScope.backgroundScope,
- mainDispatcher = testDispatcher,
- backgroundDispatcher = testDispatcher,
+ mainDispatcher = utils.testDispatcher,
+ backgroundDispatcher = utils.testDispatcher,
manager = manager,
repository = userRepository,
deviceProvisionedController = deviceProvisionedController,
@@ -1150,7 +1135,7 @@
resetOrExitSessionReceiver = resetOrExitSessionReceiver,
),
uiEventLogger = uiEventLogger,
- featureFlags = featureFlags,
+ featureFlags = utils.featureFlags,
userRestrictionChecker = mock(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 2e191b1..a580600 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -102,9 +102,12 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository;
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
+import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository;
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.model.SysUiState;
@@ -126,6 +129,7 @@
import com.android.systemui.shade.ShadeWindowLogger;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.NotificationEntryHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -330,6 +334,8 @@
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
+ private UserTracker mUserTracker;
+ @Mock
private NotifPipelineFlags mNotifPipelineFlags;
@Mock
private Icon mAppBubbleIcon;
@@ -429,7 +435,16 @@
keyguardInteractor,
featureFlags,
shadeRepository,
- powerInteractor);
+ powerInteractor,
+ () ->
+ new InWindowLauncherUnlockAnimationInteractor(
+ new InWindowLauncherUnlockAnimationRepository(),
+ mTestScope.getBackgroundScope(),
+ keyguardTransitionInteractor,
+ () -> new FakeKeyguardSurfaceBehindRepository(),
+ mock(ActivityManagerWrapper.class)
+ )
+ );
mFromPrimaryBouncerTransitionInteractor = new FromPrimaryBouncerTransitionInteractor(
keyguardTransitionRepository,
@@ -475,6 +490,7 @@
mKeyguardViewMediator,
mKeyguardBypassController,
syncExecutor,
+ syncExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
@@ -482,7 +498,8 @@
mAuthController,
() -> mShadeInteractor,
mShadeWindowLogger,
- () -> mSelectedUserInteractor
+ () -> mSelectedUserInteractor,
+ mUserTracker
);
mNotificationShadeWindowController.fetchWindowRootView();
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index 8353cf7..d0fa27e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -20,6 +20,7 @@
import dagger.Binds
import dagger.Module
import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -31,16 +32,23 @@
private val _onAnyConfigurationChange = MutableSharedFlow<Unit>()
override val onAnyConfigurationChange: Flow<Unit> = _onAnyConfigurationChange.asSharedFlow()
+ private val _onConfigurationChange =
+ MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ override val onConfigurationChange: Flow<Unit> = _onConfigurationChange.asSharedFlow()
+
private val _scaleForResolution = MutableStateFlow(1f)
override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow()
private val pixelSizes = mutableMapOf<Int, MutableStateFlow<Int>>()
- private val colors = mutableMapOf<Int, MutableStateFlow<Int>>()
fun onAnyConfigurationChange() {
_onAnyConfigurationChange.tryEmit(Unit)
}
+ fun onConfigurationChange() {
+ _onConfigurationChange.tryEmit(Unit)
+ }
+
fun setScaleForResolution(scale: Float) {
_scaleForResolution.value = scale
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
new file mode 100644
index 0000000..3ab1b6c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeCommunalMediaRepository(
+ override val mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+) : CommunalMediaRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index 799bb40..2cb17b5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -1,6 +1,7 @@
package com.android.systemui.communal.data.repository
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
/** Fake implementation of [CommunalRepository]. */
@@ -16,4 +17,11 @@
fun setIsCommunalEnabled(value: Boolean) {
isCommunalEnabled = value
}
+
+ private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
+
+ fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
+ _isCommunalHubShowing.value = isCommunalHubShowing
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index 08adda3..8a2ff50 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -1,6 +1,5 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.communal.data.model.CommunalWidgetMetadata
import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import kotlinx.coroutines.flow.Flow
@@ -10,7 +9,6 @@
class FakeCommunalWidgetRepository : CommunalWidgetRepository {
private val _stopwatchAppWidgetInfo = MutableStateFlow<CommunalAppWidgetInfo?>(null)
override val stopwatchAppWidgetInfo: Flow<CommunalAppWidgetInfo?> = _stopwatchAppWidgetInfo
- override var communalWidgetAllowlist: List<CommunalWidgetMetadata> = emptyList()
private val _communalWidgets = MutableStateFlow<List<CommunalWidgetContentModel>>(emptyList())
override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = _communalWidgets
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
new file mode 100644
index 0000000..0c821ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.domain.interactor
+
+import android.appwidget.AppWidgetHost
+import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+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.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.TestScope
+
+object CommunalInteractorFactory {
+
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ testScope: TestScope = TestScope(),
+ communalRepository: FakeCommunalRepository = FakeCommunalRepository(),
+ widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(),
+ mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(),
+ smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
+ tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
+ appWidgetHost: AppWidgetHost = mock(),
+ ): WithDependencies {
+ val withDeps =
+ CommunalTutorialInteractorFactory.create(
+ testScope = testScope,
+ communalTutorialRepository = tutorialRepository,
+ communalRepository = communalRepository,
+ )
+ return WithDependencies(
+ communalRepository,
+ widgetRepository,
+ mediaRepository,
+ smartspaceRepository,
+ tutorialRepository,
+ withDeps.keyguardRepository,
+ withDeps.keyguardInteractor,
+ withDeps.communalTutorialInteractor,
+ appWidgetHost,
+ CommunalInteractor(
+ communalRepository,
+ widgetRepository,
+ mediaRepository,
+ smartspaceRepository,
+ withDeps.communalTutorialInteractor,
+ appWidgetHost,
+ ),
+ )
+ }
+
+ data class WithDependencies(
+ val communalRepository: FakeCommunalRepository,
+ val widgetRepository: FakeCommunalWidgetRepository,
+ val mediaRepository: FakeCommunalMediaRepository,
+ val smartspaceRepository: FakeSmartspaceRepository,
+ val tutorialRepository: FakeCommunalTutorialRepository,
+ val keyguardRepository: FakeKeyguardRepository,
+ val keyguardInteractor: KeyguardInteractor,
+ val tutorialInteractor: CommunalTutorialInteractor,
+ val appWidgetHost: AppWidgetHost,
+ val communalInteractor: CommunalInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorFactory.kt
new file mode 100644
index 0000000..e5cadab
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorFactory.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.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import kotlinx.coroutines.test.TestScope
+
+object CommunalTutorialInteractorFactory {
+
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ testScope: TestScope,
+ communalTutorialRepository: FakeCommunalTutorialRepository =
+ FakeCommunalTutorialRepository(),
+ communalRepository: FakeCommunalRepository =
+ FakeCommunalRepository(isCommunalEnabled = true),
+ keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
+ keyguardInteractor: KeyguardInteractor =
+ KeyguardInteractorFactory.create(
+ repository = keyguardRepository,
+ )
+ .keyguardInteractor
+ ): WithDependencies {
+ return WithDependencies(
+ testScope = testScope,
+ communalRepository = communalRepository,
+ communalTutorialRepository = communalTutorialRepository,
+ keyguardRepository = keyguardRepository,
+ keyguardInteractor = keyguardInteractor,
+ communalTutorialInteractor =
+ CommunalTutorialInteractor(
+ testScope.backgroundScope,
+ communalTutorialRepository,
+ keyguardInteractor,
+ communalRepository,
+ )
+ )
+ }
+
+ data class WithDependencies(
+ val testScope: TestScope,
+ val communalRepository: FakeCommunalRepository,
+ val communalTutorialRepository: FakeCommunalTutorialRepository,
+ val keyguardRepository: FakeKeyguardRepository,
+ val keyguardInteractor: KeyguardInteractor,
+ val communalTutorialInteractor: CommunalTutorialInteractor,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt
index abf72af..6838e76 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/FakeKeyguardDataLayerModule.kt
@@ -17,6 +17,7 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueueModule
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepositoryModule
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepositoryModule
import dagger.Module
@@ -26,6 +27,7 @@
FakeCommandQueueModule::class,
FakeKeyguardRepositoryModule::class,
FakeKeyguardTransitionRepositoryModule::class,
+ FakeKeyguardSurfaceBehindRepositoryModule::class,
]
)
object FakeKeyguardDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt
index 823f29a..70de05f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardSurfaceBehindRepository.kt
@@ -16,14 +16,31 @@
package com.android.systemui.keyguard.data.repository
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeKeyguardSurfaceBehindRepository : KeyguardSurfaceBehindRepository {
+@SysUISingleton
+class FakeKeyguardSurfaceBehindRepository @Inject constructor() : KeyguardSurfaceBehindRepository {
private val _isAnimatingSurface = MutableStateFlow(false)
override val isAnimatingSurface = _isAnimatingSurface.asStateFlow()
+ private val _isSurfaceAvailable = MutableStateFlow(false)
+ override val isSurfaceRemoteAnimationTargetAvailable = _isSurfaceAvailable.asStateFlow()
+
override fun setAnimatingSurface(animating: Boolean) {
_isAnimatingSurface.value = animating
}
+
+ override fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) {
+ _isSurfaceAvailable.value = available
+ }
+}
+
+@Module
+interface FakeKeyguardSurfaceBehindRepositoryModule {
+ @Binds fun bindFake(fake: FakeKeyguardSurfaceBehindRepository): KeyguardSurfaceBehindRepository
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
index 957fbbd..ace6500 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
@@ -18,11 +18,11 @@
package com.android.systemui.power.data.repository
import android.os.PowerManager
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.dagger.SysUISingleton
import dagger.Binds
import dagger.Module
import javax.inject.Inject
@@ -55,15 +55,15 @@
lastWakeReason = wakeReason
}
- override fun userTouch() {
+ override fun userTouch(noChangeLights: Boolean) {
userTouchRegistered = true
}
override fun updateWakefulness(
- rawState: WakefulnessState,
- lastWakeReason: WakeSleepReason,
- lastSleepReason: WakeSleepReason,
- powerButtonLaunchGestureTriggered: Boolean
+ rawState: WakefulnessState,
+ lastWakeReason: WakeSleepReason,
+ lastSleepReason: WakeSleepReason,
+ powerButtonLaunchGestureTriggered: Boolean
) {
_wakefulness.value =
WakefulnessModel(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index 10110e2..36ec18f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -16,9 +16,15 @@
package com.android.systemui.scene
+import android.app.ActivityTaskManager
+import android.content.Context
+import android.content.Intent
import android.content.pm.UserInfo
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
+import android.telecom.TelecomManager
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.model.AuthenticationMethodModel as DataLayerAuthenticationMethodModel
import com.android.systemui.authentication.data.repository.AuthenticationRepository
@@ -26,8 +32,11 @@
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel as DomainLayerAuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
+import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.EmergencyDialerIntentFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -35,11 +44,12 @@
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.doze.DozeLogger
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
@@ -61,10 +71,13 @@
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
+import com.android.systemui.telephony.data.repository.TelephonyRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.user.ui.viewmodel.UserActionViewModel
import com.android.systemui.user.ui.viewmodel.UserViewModel
import com.android.systemui.util.mockito.mock
@@ -81,8 +94,10 @@
*/
@OptIn(ExperimentalCoroutinesApi::class)
class SceneTestUtils(
- test: SysuiTestCase,
+ private val context: Context,
) {
+ constructor(test: SysuiTestCase) : this(context = test.context)
+
val kosmos = Kosmos()
val testDispatcher = kosmos.testDispatcher
val testScope = kosmos.testScope
@@ -99,15 +114,23 @@
currentTime = { testScope.currentTime },
)
}
+ val configurationRepository: FakeConfigurationRepository by lazy {
+ FakeConfigurationRepository()
+ }
+ private val emergencyServicesRepository: EmergencyServicesRepository by lazy {
+ EmergencyServicesRepository(
+ applicationScope = applicationScope(),
+ resources = context.resources,
+ configurationRepository = configurationRepository,
+ )
+ }
+ val telephonyRepository: FakeTelephonyRepository by lazy { FakeTelephonyRepository() }
val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
- private val communalWidgetRepository: FakeCommunalWidgetRepository by lazy {
- FakeCommunalWidgetRepository()
- }
val keyguardRepository: FakeKeyguardRepository by lazy { FakeKeyguardRepository() }
val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
- private val userRepository: UserRepository by lazy {
+ val userRepository: UserRepository by lazy {
FakeUserRepository().apply {
val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
setUserInfos(users)
@@ -115,8 +138,6 @@
}
}
- private val context = test.context
-
private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() }
private var falsingInteractor: FalsingInteractor? = null
@@ -185,7 +206,7 @@
featureFlags = featureFlags,
sceneContainerFlags = sceneContainerFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
- configurationRepository = FakeConfigurationRepository(),
+ configurationRepository = configurationRepository,
shadeRepository = FakeShadeRepository(),
sceneInteractorProvider = { sceneInteractor() },
powerInteractor = PowerInteractorFactory.create().powerInteractor,
@@ -193,10 +214,10 @@
}
fun communalInteractor(): CommunalInteractor {
- return CommunalInteractor(
- communalRepository = communalRepository,
- widgetRepository = communalWidgetRepository,
- )
+ return CommunalInteractorFactory.create(
+ communalRepository = communalRepository,
+ )
+ .communalInteractor
}
fun bouncerInteractor(
@@ -219,6 +240,7 @@
fun bouncerViewModel(
bouncerInteractor: BouncerInteractor,
authenticationInteractor: AuthenticationInteractor,
+ actionButtonInteractor: BouncerActionButtonInteractor,
users: List<UserViewModel> = createUsers(),
): BouncerViewModel {
return BouncerViewModel(
@@ -231,13 +253,16 @@
selectedUser = flowOf(users.first { it.isSelectionMarkerVisible }),
users = flowOf(users),
userSwitcherMenu = flowOf(createMenuActions()),
- telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
+ actionButtonInteractor = actionButtonInteractor,
)
}
+ fun telephonyInteractor(
+ repository: TelephonyRepository = telephonyRepository,
+ ): TelephonyInteractor {
+ return TelephonyInteractor(repository = repository)
+ }
+
fun falsingInteractor(collector: FalsingCollector = falsingCollector()): FalsingInteractor {
return falsingInteractor ?: FalsingInteractor(collector).also { falsingInteractor = it }
}
@@ -287,6 +312,40 @@
}
}
+ fun selectedUserInteractor(): SelectedUserInteractor {
+ return SelectedUserInteractor(userRepository, featureFlags)
+ }
+
+ fun bouncerActionButtonInteractor(
+ mobileConnectionsRepository: MobileConnectionsRepository = mock(),
+ activityTaskManager: ActivityTaskManager = mock(),
+ telecomManager: TelecomManager? = null,
+ emergencyAffordanceManager: EmergencyAffordanceManager =
+ EmergencyAffordanceManager(context),
+ emergencyDialerIntentFactory: EmergencyDialerIntentFactory =
+ object : EmergencyDialerIntentFactory {
+ override fun invoke(): Intent = Intent()
+ },
+ metricsLogger: MetricsLogger = mock(),
+ dozeLogger: DozeLogger = mock(),
+ ): BouncerActionButtonInteractor {
+ return BouncerActionButtonInteractor(
+ applicationContext = context,
+ backgroundDispatcher = testDispatcher,
+ repository = emergencyServicesRepository,
+ mobileConnectionsRepository = mobileConnectionsRepository,
+ telephonyInteractor = telephonyInteractor(),
+ authenticationInteractor = authenticationInteractor(),
+ selectedUserInteractor = selectedUserInteractor(),
+ activityTaskManager = activityTaskManager,
+ telecomManager = telecomManager,
+ emergencyAffordanceManager = emergencyAffordanceManager,
+ emergencyDialerIntentFactory = emergencyDialerIntentFactory,
+ metricsLogger = metricsLogger,
+ dozeLogger = dozeLogger,
+ )
+ }
+
companion object {
fun DomainLayerAuthenticationMethodModel.toDataLayer(): DataLayerAuthenticationMethodModel {
return when (this) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
new file mode 100644
index 0000000..c8013ef
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.smartspace.data.repository
+
+import android.app.smartspace.SmartspaceTarget
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeSmartspaceRepository(
+ smartspaceRemoteViewsEnabled: Boolean = true,
+) : SmartspaceRepository {
+
+ override val isSmartspaceRemoteViewsEnabled = smartspaceRemoteViewsEnabled
+
+ private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
+ MutableStateFlow(emptyList())
+ override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
+ _lockscreenSmartspaceTargets
+
+ fun setLockscreenSmartspaceTargets(targets: List<SmartspaceTarget>) {
+ _lockscreenSmartspaceTargets.value = targets
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt
index 992ac62..5cde386 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt
@@ -23,6 +23,7 @@
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
@@ -31,6 +32,9 @@
private val _callState = MutableStateFlow(0)
override val callState: Flow<Int> = _callState.asStateFlow()
+ private val _isInCall = MutableStateFlow(false)
+ override val isInCall: StateFlow<Boolean> = _isInCall.asStateFlow()
+
override var hasTelephonyRadio: Boolean = true
private set
@@ -38,8 +42,12 @@
_callState.value = value
}
- fun setHasRadio(hasRadio: Boolean) {
- this.hasTelephonyRadio = hasRadio
+ fun setIsInCall(isInCall: Boolean) {
+ _isInCall.value = isInCall
+ }
+
+ fun setHasTelephonyRadio(hasTelephonyRadio: Boolean) {
+ this.hasTelephonyRadio = hasTelephonyRadio
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/reference/FakeWeakReferenceFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/reference/FakeWeakReferenceFactory.kt
new file mode 100644
index 0000000..f0a8fd0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/reference/FakeWeakReferenceFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.reference
+
+import java.lang.ref.WeakReference
+
+class FakeWeakReferenceFactory : WeakReferenceFactory {
+ private val managedReferents = mutableListOf<WeakReference<*>>()
+
+ override fun <T> create(referent: T): WeakReference<T> {
+ val weakRef = WeakReference(referent)
+ managedReferents.add(weakRef)
+ return weakRef
+ }
+
+ /**
+ * Clears any [WeakReference] objects pointing to this object. If argument is null, clears all
+ * references.
+ */
+ fun <T> clear(referent: T) {
+ managedReferents.filter { it.get() == referent }.forEach { it.clear() }
+ }
+}
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 25814ec..9cda267 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -59,8 +59,11 @@
AppNotResponding anr = 4;
JavaCrash java_crash = 5;
NativeCrash native_crash = 6;
+ SystemServerStarted system_server_started = 7;
}
+ message SystemServerStarted {}
+
message Watchdog {
// The watchdog subject.
// Required.
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index d0e442e..5c9bf18 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -32,3 +32,14 @@
host_supported: true,
visibility: ["//visibility:public"],
}
+
+java_library {
+ name: "ravenwood-junit",
+ srcs: ["junit-src/**/*.java"],
+ libs: [
+ "junit",
+ ],
+ sdk_version: "core_current",
+ host_supported: true,
+ visibility: ["//visibility:public"],
+}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
new file mode 100644
index 0000000..0aac084
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
@@ -0,0 +1,33 @@
+/*
+ * 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.platform.test.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IgnoreUnderRavenwood {
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
new file mode 100644
index 0000000..a6b3f66
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -0,0 +1,50 @@
+/*
+ * 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.platform.test.ravenwood;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+
+import org.junit.Assume;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * THIS RULE IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+public class RavenwoodRule implements TestRule {
+ public boolean isUnderRavenwood() {
+ // TODO: give ourselves a better environment signal
+ return System.getProperty("java.class.path").contains("ravenwood");
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
+ Assume.assumeFalse(isUnderRavenwood());
+ }
+ base.evaluate();
+ }
+ };
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
index 46b4628..9c6dfc6 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java
@@ -31,7 +31,7 @@
* This class matches multi-finger multi-tap gestures. The number of fingers and the number of taps
* for each instance is specified in the constructor.
*/
-class MultiFingerMultiTap extends GestureMatcher {
+public class MultiFingerMultiTap extends GestureMatcher {
// The target number of taps.
final int mTargetTapCount;
@@ -56,7 +56,7 @@
* @throws IllegalArgumentException if <code>fingers<code/> is less than 2
* or <code>taps<code/> is not positive.
*/
- MultiFingerMultiTap(
+ public MultiFingerMultiTap(
Context context,
int fingers,
int taps,
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
index 9c54100..f586036f 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -23,9 +23,9 @@
* This class matches gestures of the form multi-finger multi-tap and hold. The number of fingers
* and taps for each instance is specified in the constructor.
*/
-class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
+public class MultiFingerMultiTapAndHold extends MultiFingerMultiTap {
- MultiFingerMultiTapAndHold(
+ public MultiFingerMultiTapAndHold(
Context context,
int fingers,
int taps,
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 5953d0d..36e75118 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -41,6 +41,8 @@
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.Flags;
import com.android.server.accessibility.gestures.GestureMatcher;
+import com.android.server.accessibility.gestures.MultiFingerMultiTap;
+import com.android.server.accessibility.gestures.MultiFingerMultiTapAndHold;
import com.android.server.accessibility.gestures.MultiTap;
import com.android.server.accessibility.gestures.MultiTapAndHold;
@@ -476,6 +478,15 @@
null));
mGestureMatchers.add(new TwoFingersDownOrSwipe(context));
+ if (mDetectTwoFingerTripleTap) {
+ mGestureMatchers.add(new MultiFingerMultiTap(context, /* fingers= */ 2,
+ /* taps= */ 3, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP,
+ null));
+ mGestureMatchers.add(new MultiFingerMultiTapAndHold(context, /* fingers= */ 2,
+ /* taps= */ 3, MagnificationGestureMatcher.GESTURE_TRIPLE_TAP_AND_HOLD,
+ null));
+ }
+
mGesturesObserver = new MagnificationGesturesObserver(this,
mGestureMatchers.toArray(new GestureMatcher[mGestureMatchers.size()]));
} else {
@@ -512,7 +523,9 @@
@Override
public boolean shouldStopDetection(MotionEvent motionEvent) {
return !mWindowMagnificationMgr.isWindowMagnifierEnabled(mDisplayId)
- && !mDetectSingleFingerTripleTap;
+ && !mDetectSingleFingerTripleTap
+ && !(mDetectTwoFingerTripleTap
+ && Flags.enableMagnificationMultipleFingerMultipleTapGesture());
}
@Override
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index b37bbd6..ab678d9 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -20,3 +20,10 @@
description: "Mitigation for relayout issue"
bug: "294330426"
}
+
+flag {
+ name: "ignore_view_state_reset_to_empty"
+ namespace: "autofill"
+ description: "Mitigation for view state reset to empty causing no save dialog to show issue"
+ bug: "297976948"
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a14f3fe..4e49c6e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -199,6 +199,7 @@
"biometrics_flags_lib",
"am_flags_lib",
"com_android_wm_shell_flags_lib",
+ "android.app.flags-aconfig-java"
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 4f32220..065a447 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1421,7 +1421,7 @@
@UserIdInt int userId);
/**
- * Sends the PACKAGE_RESTARTED broadcast on the package manager handler thread.
+ * Sends the PACKAGE_RESTARTED broadcast.
*/
public abstract void sendPackageRestartedBroadcast(@NonNull String packageName,
int uid, @Intent.Flags int flags);
@@ -1431,4 +1431,10 @@
*/
public abstract ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(
int userId);
+
+ /**
+ * Sends the ACTION_PACKAGE_DATA_CLEARED broadcast.
+ */
+ public abstract void sendPackageDataClearedBroadcast(@NonNull String packageName,
+ int uid, int userId, boolean isRestore, boolean isInstantApp);
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index c4cb816..256f2b3 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -180,14 +180,7 @@
# Snapshot rebuild instance
3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3)
# Caller information to clear application data
-1003160 pm_clear_app_data_caller (pid|1),(uid|1),(package|3)
-# ---------------------------
-# Installer.java
-# ---------------------------
-# Caller Information to clear application data
-1003200 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1)
-# Call stack to clear application data
-1003201 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1)
+3132 pm_clear_app_data_caller (pid|1),(uid|1),(package|3)
# ---------------------------
# InputMethodManagerService.java
@@ -227,6 +220,14 @@
35000 auto_brightness_adj (old_lux|5),(old_brightness|5),(new_lux|5),(new_brightness|5)
# ---------------------------
+# Installer.java
+# ---------------------------
+# Caller Information to clear application data
+39000 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1)
+# Call stack to clear application data
+39001 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1)
+
+# ---------------------------
# ConnectivityService.java
# ---------------------------
# Connectivity state changed
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0223509..15fc2dc 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -61,7 +61,6 @@
import android.app.PendingIntent;
import android.app.admin.SecurityLog;
import android.app.usage.StorageStatsManager;
-import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -2139,13 +2138,8 @@
| MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER,
userId, Process.myUid())) {
try {
- final AttributionSource attributionSource = new AttributionSource.Builder(ai.uid)
- .setPackageName(ai.packageName)
- .build();
- boolean hasLegacy =
- mIAppOpsService.checkOperationWithState(
- OP_LEGACY_STORAGE, attributionSource.asState())
- == MODE_ALLOWED;
+ boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid,
+ ai.packageName) == MODE_ALLOWED;
updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e);
@@ -4546,11 +4540,8 @@
// sharing the uid and allow same level of storage access for all packages even if
// one of the packages has the appop granted.
for (String uidPackageName : packagesForUid) {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(uidPackageName).build();
- if (mIAppOpsService.checkOperationWithState(
- OP_REQUEST_INSTALL_PACKAGES, attributionSource.asState())
- == MODE_ALLOWED) {
+ if (mIAppOpsService.checkOperation(
+ OP_REQUEST_INSTALL_PACKAGES, uid, uidPackageName) == MODE_ALLOWED) {
hasInstallOp = true;
break;
}
@@ -4847,11 +4838,8 @@
@Override
public boolean hasExternalStorageAccess(int uid, String packageName) {
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(packageName).build();
- final int opMode =
- mIAppOpsService.checkOperationWithState(
- OP_MANAGE_EXTERNAL_STORAGE, attributionSource.asState());
+ final int opMode = mIAppOpsService.checkOperation(
+ OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
if (opMode == AppOpsManager.MODE_DEFAULT) {
return mIPackageManager.checkUidPermission(
MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index afc0dd1..cd9c53b 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -83,6 +83,9 @@
}
],
"file_patterns": ["VpnManagerService\\.java"]
+ },
+ {
+ "name": "FrameworksNetTests"
}
],
"presubmit-large": [
@@ -124,9 +127,6 @@
},
{
"name": "CtsSuspendAppsTestCases"
- },
- {
- "name": "FrameworksNetTests"
}
]
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4bdb4da..5f1a7e7 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -167,7 +167,6 @@
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
-import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
import android.content.Context;
@@ -1101,12 +1100,8 @@
SystemClock.uptimeMillis()); // Use current time, not lastActivity.
}
}
- final AttributionSource attributionSource = new AttributionSource.Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.startOperationWithState(AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND,
- attributionSource.asState(),
+ mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
true, false, null, false, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
@@ -2456,15 +2451,10 @@
stopProcStatsOp = false;
}
- final AttributionSource attributionSource = new AttributionSource
- .Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.startOperationWithState(
+ mAm.mAppOpsService.startOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, attributionSource.asState(),
- true, false, "", false,
- AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
+ null, true, false, "", false, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
@@ -2524,13 +2514,10 @@
if (alreadyStartedOp) {
// If we had previously done a start op for direct foreground start,
// we have cleared the flag so can now drop it.
- final AttributionSource attributionSource = new AttributionSource
- .Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.finishOperationWithState(
+ mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
+ null);
}
}
} else {
@@ -2573,13 +2560,9 @@
SystemClock.uptimeMillis());
}
}
- final AttributionSource attributionSource =
- new AttributionSource.Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.finishOperationWithState(
+ mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
unregisterAppOpCallbackLocked(r);
logFGSStateChangeLocked(r,
FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
@@ -5721,12 +5704,8 @@
SystemClock.uptimeMillis());
}
}
- final AttributionSource attributionSource = new AttributionSource
- .Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.finishOperationWithState(AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
+ mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
mServiceFGAnrTimer.cancel(r);
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
@@ -5791,13 +5770,9 @@
SystemClock.uptimeMillis());
}
}
- final AttributionSource attributionSource = new AttributionSource
- .Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.finishOperationWithState(
+ mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
unregisterAppOpCallbackLocked(r);
r.mFgsExitTime = SystemClock.uptimeMillis();
logFGSStateChangeLocked(r,
@@ -8516,11 +8491,8 @@
mAm.mBatteryStatsService.noteServiceStartRunning(callingUid, callingPackage,
cn.getClassName());
- final AttributionSource attributionSource = new AttributionSource.Builder(r.appInfo.uid)
- .setPackageName(r.packageName)
- .build();
- mAm.mAppOpsService.startOperationWithState(AppOpsManager.getToken(mAm.mAppOpsService),
- AppOpsManager.OP_START_FOREGROUND, attributionSource.asState(),
+ mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
+ AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
true, false, null, false,
AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1566113..b99a98f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -79,6 +79,7 @@
import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
+import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP;
@@ -428,11 +429,10 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintFunction;
-import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.AlarmManagerInternal;
import com.android.server.BootReceiver;
@@ -3151,11 +3151,8 @@
}
private boolean hasUsageStatsPermission(String callingPackage, int callingUid, int callingPid) {
- final AttributionSource attributionSource = new AttributionSource.Builder(callingUid)
- .setPackageName(callingPackage)
- .build();
- final int mode = mAppOpsService.noteOperationWithState(AppOpsManager.OP_GET_USAGE_STATS,
- attributionSource.asState(), false, "", false).getOpMode();
+ final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
+ callingUid, callingPackage, null, false, "", false).getOpMode();
if (mode == AppOpsManager.MODE_DEFAULT) {
return checkPermission(Manifest.permission.PACKAGE_USAGE_STATS, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
@@ -3633,30 +3630,10 @@
}
if (succeeded) {
- final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
- Uri.fromParts("package", packageName, null /* fragment */));
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
- | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(Intent.EXTRA_UID,
- (appInfo != null) ? appInfo.uid : INVALID_UID);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
- if (isRestore) {
- intent.putExtra(Intent.EXTRA_IS_RESTORE, true);
- }
- if (isInstantApp) {
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- }
- final int[] visibilityAllowList = mPackageManagerInt.getVisibilityAllowList(
- packageName, resolvedUserId);
- broadcastIntentInPackage("android", null /* featureId */,
- SYSTEM_UID, uid, pid, intent, null /* resolvedType */,
- null /* resultToApp */, null /* resultTo */, 0 /* resultCode */,
- null /* resultData */, null /* resultExtras */,
- isInstantApp ? permission.ACCESS_INSTANT_APPS : null,
- null /* bOptions */, false /* serialized */, false /* sticky */,
- resolvedUserId, BackgroundStartPrivileges.NONE,
- visibilityAllowList);
+ mPackageManagerInt.sendPackageDataClearedBroadcast(packageName,
+ ((appInfo != null) ? appInfo.uid : INVALID_UID), resolvedUserId,
+ isRestore, isInstantApp);
}
if (observer != null) {
@@ -4177,29 +4154,7 @@
flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND;
}
- if (android.content.pm.Flags.stayStopped()) {
- // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
- mPackageManagerInt.sendPackageRestartedBroadcast(packageName,
- uid, flags);
- } else {
- Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
- Uri.fromParts("package", packageName, null));
- intent.addFlags(flags);
- final int userId = UserHandle.getUserId(uid);
- final int[] broadcastAllowList =
- getPackageManagerInternal().getVisibilityAllowList(packageName, userId);
- intent.putExtra(Intent.EXTRA_UID, uid);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
- null /* callerFeatureId */, intent, null /* resolvedType */,
- null /* resultToApp */, null /* resultTo */,
- 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
- null /* requiredPermissions */, null /* excludedPermissions */,
- null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
- false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
- Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
- broadcastAllowList, null /* filterExtrasForReceiver */);
- }
+ mPackageManagerInt.sendPackageRestartedBroadcast(packageName, uid, flags);
}
private void cleanupDisabledPackageComponentsLocked(
@@ -5937,18 +5892,9 @@
@Override
public int noteOp(String op, int uid, String packageName) {
// TODO moltmann: Allow to specify featureId
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .build();
- return mActivityManagerService
- .mAppOpsService
- .noteOperationWithState(
- AppOpsManager.strOpToOp(op),
- attributionSource.asState(),
- false,
- "",
- false)
- .getOpMode();
+ return mActivityManagerService.mAppOpsService
+ .noteOperation(AppOpsManager.strOpToOp(op), uid, packageName, null,
+ false, "", false).getOpMode();
}
@Override
@@ -7715,14 +7661,100 @@
"getUidProcessState", callingPackage); // Ignore return value
synchronized (mProcLock) {
- if (mPendingStartActivityUids.isPendingTopUid(uid)) {
- return PROCESS_STATE_TOP;
- }
- return mProcessList.getUidProcStateLOSP(uid);
+ return getUidProcessStateInnerLOSP(uid);
}
}
@Override
+ public int getBindingUidProcessState(int targetUid, String callingPackage) {
+ if (!hasUsageStatsPermission(callingPackage)) {
+ enforceCallingPermission(android.Manifest.permission.GET_BINDING_UID_IMPORTANCE,
+ "getBindingUidProcessState");
+ }
+ // We don't need to do a cross-user check here (unlike getUidProcessState),
+ // because we only allow to see UIDs that are actively communicating with the caller.
+
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final boolean allowed = (callingUid == targetUid)
+ || hasServiceBindingOrProviderUseLocked(callingUid, targetUid);
+ if (!allowed) {
+ return PROCESS_STATE_NONEXISTENT;
+ }
+ return getUidProcessStateInnerLOSP(targetUid);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @GuardedBy(anyOf = {"this", "mProcLock"})
+ private int getUidProcessStateInnerLOSP(int uid) {
+ if (mPendingStartActivityUids.isPendingTopUid(uid)) {
+ return PROCESS_STATE_TOP;
+ }
+ return mProcessList.getUidProcStateLOSP(uid);
+ }
+
+ /**
+ * Ensure that {@code clientUid} has a bound service client to {@code callingUid}
+ */
+ @GuardedBy("this")
+ private boolean hasServiceBindingOrProviderUseLocked(int callingUid, int clientUid) {
+ // See if there's a service binding
+ final Boolean hasBinding = mProcessList.searchEachLruProcessesLOSP(
+ false, pr -> {
+ if (pr.uid == callingUid) {
+ final ProcessServiceRecord psr = pr.mServices;
+ final int serviceCount = psr.mServices.size();
+ for (int svc = 0; svc < serviceCount; svc++) {
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
+ psr.mServices.valueAt(svc).getConnections();
+ final int size = conns.size();
+ for (int conni = 0; conni < size; conni++) {
+ final ArrayList<ConnectionRecord> crs = conns.valueAt(conni);
+ for (int con = 0; con < crs.size(); con++) {
+ final ConnectionRecord cr = crs.get(con);
+ final ProcessRecord clientPr = cr.binding.client;
+
+ if (clientPr.uid == clientUid) {
+ return Boolean.TRUE;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ });
+ if (Boolean.TRUE.equals(hasBinding)) {
+ return true;
+ }
+
+ final Boolean hasProviderClient = mProcessList.searchEachLruProcessesLOSP(
+ false, pr -> {
+ if (pr.uid == callingUid) {
+ final ProcessProviderRecord ppr = pr.mProviders;
+ for (int provi = ppr.numberOfProviders() - 1; provi >= 0; provi--) {
+ ContentProviderRecord cpr = ppr.getProviderAt(provi);
+
+ for (int i = cpr.connections.size() - 1; i >= 0; i--) {
+ ContentProviderConnection conn = cpr.connections.get(i);
+ ProcessRecord client = conn.client;
+ if (client.uid == clientUid) {
+ return Boolean.TRUE;
+ }
+ }
+ }
+ }
+ return null;
+ });
+
+ return Boolean.TRUE.equals(hasProviderClient);
+ }
+
+ @Override
public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
@@ -20076,26 +20108,20 @@
}
@Override
- public int checkOperation(int code, AttributionSource attributionSource, boolean raw,
- TriFunction<Integer, AttributionSource, Boolean, Integer> superImpl) {
- final int uid = attributionSource.getUid();
-
+ public int checkOperation(int code, int uid, String packageName,
+ String attributionTag, boolean raw,
+ QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
- final AttributionSource shellAttributionSource =
- new AttributionSource.Builder(shellUid)
- .setPackageName("com.android.shell")
- .build();
-
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, shellAttributionSource, raw);
+ return superImpl.apply(code, shellUid, "com.android.shell", null, raw);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, attributionSource, raw);
+ return superImpl.apply(code, uid, packageName, attributionTag, raw);
}
@Override
@@ -20115,30 +20141,23 @@
}
@Override
- public SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
- boolean shouldCollectAsyncNotedOp,
+ public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
+ @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
- @NonNull QuintFunction<Integer, AttributionSource, Boolean, String, Boolean,
+ @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
SyncNotedAppOp> superImpl) {
- final int uid = attributionSource.getUid();
- final String attributionTag = attributionSource.getAttributionTag();
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
- final AttributionSource shellAttributionSource =
- new AttributionSource.Builder(shellUid)
- .setPackageName("com.android.shell")
- .setAttributionTag(attributionTag)
- .build();
try {
- return superImpl.apply(code, shellAttributionSource,
+ return superImpl.apply(code, shellUid, "com.android.shell", featureId,
shouldCollectAsyncNotedOp, message, shouldCollectMessage);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
+ return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
message, shouldCollectMessage);
}
@@ -20169,37 +20188,28 @@
}
@Override
- public SyncNotedAppOp startOperation(IBinder token, int code,
- AttributionSource attributionSource,
+ public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage,
@AttributionFlags int attributionFlags, int attributionChainId,
- @NonNull NonaFunction<IBinder, Integer, AttributionSource, Boolean,
+ @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
- final int uid = attributionSource.getUid();
- final String attributionTag = attributionSource.getAttributionTag();
-
if (uid == mTargetUid && isTargetOp(code)) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
try {
- final AttributionSource shellAttributionSource =
- new AttributionSource.Builder(shellUid)
- .setPackageName("com.android.shell")
- .setAttributionTag(attributionTag)
- .build();
-
- return superImpl.apply(token, code, shellAttributionSource,
- startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ return superImpl.apply(token, code, shellUid, "com.android.shell",
+ attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(token, code, attributionSource, startIfModeDefault,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
- attributionChainId);
+ return superImpl.apply(token, code, uid, packageName, attributionTag,
+ startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+ attributionFlags, attributionChainId);
}
@Override
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index 947fcd3..18a9153 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -37,7 +37,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
-import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.OnPermissionsChangedListener;
@@ -193,11 +192,7 @@
if (DEBUG_PERMISSION_TRACKER) {
final IAppOpsService appOpsService = mInjector.getIAppOpsService();
try {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .build();
- final int mode =
- appOpsService.checkOperationWithState(op, attributionSource.asState());
+ final int mode = appOpsService.checkOperation(op, uid, packageName);
Slog.i(TAG, "onOpChanged: " + opToPublicName(op)
+ " " + UserHandle.formatUid(uid)
+ " " + packageName + " " + mode);
@@ -312,11 +307,7 @@
final IAppOpsService appOpsService = mInjector.getIAppOpsService();
for (String pkg : packages) {
try {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(mUid).setPackageName(pkg).build();
- final int mode =
- appOpsService.checkOperationWithState(
- mAppOp, attributionSource.asState());
+ final int mode = appOpsService.checkOperation(mAppOp, mUid, pkg);
if (mode == AppOpsManager.MODE_ALLOWED) {
mAppOpAllowed = true;
return;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 931914f..2aed847 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -131,4 +131,4 @@
30110 am_intent_sender_redirect_user (userId|1|5)
# Caller information to clear application data
-1030002 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
+30120 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e6cdbb5..599d998 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -166,6 +166,7 @@
"safety_center",
"sensors",
"system_performance",
+ "system_sw_usb",
"test_suites",
"text",
"threadnetwork",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 87633e9..47a99fe 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2030,6 +2030,9 @@
mTargetUserId = targetUserId;
userSwitchUiEnabled = mUserSwitchUiEnabled;
}
+ if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
+ mInjector.setHasTopUi(true);
+ }
if (userSwitchUiEnabled) {
UserInfo currentUserInfo = getUserInfo(currentUserId);
Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo);
@@ -2098,6 +2101,9 @@
}
private void endUserSwitch() {
+ if (android.multiuser.Flags.useAllCpusDuringUserSwitch()) {
+ mInjector.setHasTopUi(false);
+ }
final int nextUserId;
synchronized (mLock) {
nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
@@ -3781,6 +3787,15 @@
getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
}
+ void setHasTopUi(boolean hasTopUi) {
+ try {
+ Slogf.i(TAG, "Setting hasTopUi to " + hasTopUi);
+ mService.setHasTopUi(hasTopUi);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "Failed to allow using all CPU cores", e);
+ }
+ }
+
void onSystemUserVisibilityChanged(boolean visible) {
getUserManagerInternal().onSystemUserVisibilityChanged(visible);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index d6997da..052b0c2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1143,32 +1143,22 @@
}
}, RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS);
- getPackageManagerInternal()
- .setExternalSourcesPolicy(
- new PackageManagerInternal.ExternalSourcesPolicy() {
- @Override
- public int getPackageTrustedToInstallApps(String packageName, int uid) {
- final AttributionSource attributionSource =
- new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .build();
- int appOpMode =
- checkOperationWithState(
- AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
- attributionSource.asState());
- switch (appOpMode) {
- case AppOpsManager.MODE_ALLOWED:
- return PackageManagerInternal.ExternalSourcesPolicy
- .USER_TRUSTED;
- case AppOpsManager.MODE_ERRORED:
- return PackageManagerInternal.ExternalSourcesPolicy
- .USER_BLOCKED;
- default:
- return PackageManagerInternal.ExternalSourcesPolicy
- .USER_DEFAULT;
- }
- }
- });
+ getPackageManagerInternal().setExternalSourcesPolicy(
+ new PackageManagerInternal.ExternalSourcesPolicy() {
+ @Override
+ public int getPackageTrustedToInstallApps(String packageName, int uid) {
+ int appOpMode = checkOperation(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
+ uid, packageName);
+ switch (appOpMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ return PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
+ case AppOpsManager.MODE_ERRORED:
+ return PackageManagerInternal.ExternalSourcesPolicy.USER_BLOCKED;
+ default:
+ return PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT;
+ }
+ }
+ });
}
@VisibleForTesting
@@ -2544,41 +2534,22 @@
}
}
- /** @deprecated Use {@link #checkOperationWithStateRaw} instead. */
@Override
public int checkOperationRaw(int code, int uid, String packageName,
- @Nullable String attributionTag) {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName).setAttributionTag(attributionTag).build();
- return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, true /*raw*/);
+ @Nullable String attributionTag) {
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
+ true /*raw*/);
}
@Override
- public int checkOperationWithStateRaw(int code, AttributionSourceState attributionSourceState) {
- final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
- return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, true /*raw*/);
- }
-
- /** @deprecated Use {@link #checkOperationWithState} instead. */
- @Override
public int checkOperation(int code, int uid, String packageName) {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .build();
- return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, false /*raw*/);
+ return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
+ false /*raw*/);
}
- @Override
- public int checkOperationWithState(int code, AttributionSourceState attributionSourceState) {
- final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
- return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, false /*raw*/);
- }
-
- private int checkOperationImpl(int code, AttributionSource attributionSource, boolean raw) {
+ private int checkOperationImpl(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw) {
verifyIncomingOp(code);
- final String packageName = attributionSource.getPackageName();
- final int uid = attributionSource.getUid();
- final String attributionTag = attributionSource.getAttributionTag();
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -2643,10 +2614,7 @@
if (mode != AppOpsManager.MODE_ALLOWED) {
return mode;
}
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .build();
- return checkOperationWithState(code, attributionSource.asState());
+ return checkOperation(code, uid, packageName);
}
@Override
@@ -2790,38 +2758,17 @@
proxiedFlags, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
- /** @deprecated Use {@link #noteOperationWithState} instead. */
@Override
public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage) {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .setAttributionTag(attributionTag)
- .build();
- return mCheckOpsDelegateDispatcher.noteOperation(code, attributionSource,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
+ attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
- @Override
- public SyncNotedAppOp noteOperationWithState(
- int code,
- AttributionSourceState attributionSourceState,
- boolean shouldCollectAsyncNotedOp,
- String message,
- boolean shouldCollectMessage) {
- final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
- return mCheckOpsDelegateDispatcher.noteOperation(
- code, attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
- }
-
- private SyncNotedAppOp noteOperationImpl(int code, AttributionSource attributionSource,
- boolean shouldCollectAsyncNotedOp,
+ private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage) {
- final int uid = attributionSource.getUid();
- final String packageName = attributionSource.getPackageName();
- final String attributionTag = attributionSource.getAttributionTag();
-
verifyIncomingUid(uid);
verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -3216,42 +3163,22 @@
}
}
- /** @deprecated Use {@link #startOperationWithState} instead. */
@Override
public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
- @Nullable String packageName, @Nullable String attributionTag,
- boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
- String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
- int attributionChainId) {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .setAttributionTag(attributionTag)
- .build();
- return mCheckOpsDelegateDispatcher.startOperation(token, code, attributionSource,
- startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- attributionFlags, attributionChainId);
- }
-
- @Override
- public SyncNotedAppOp startOperationWithState(IBinder token, int code,
- AttributionSourceState attributionSourceState,
+ @Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
int attributionChainId) {
- final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
- return mCheckOpsDelegateDispatcher.startOperation(token, code, attributionSource,
- startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
+ attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId);
}
- private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code,
- AttributionSource attributionSource, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, @NonNull String message,
+ private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message,
boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
int attributionChainId) {
- final String packageName = attributionSource.getPackageName();
- final int uid = attributionSource.getUid();
- final String attributionTag = attributionSource.getAttributionTag();
verifyIncomingUid(uid);
verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -3273,7 +3200,7 @@
int result = MODE_DEFAULT;
if (code == OP_RECORD_AUDIO_HOTWORD || code == OP_RECEIVE_AMBIENT_TRIGGER_AUDIO
|| code == OP_RECORD_AUDIO_SANDBOXED) {
- result = checkOperationWithState(OP_RECORD_AUDIO, attributionSource.asState());
+ result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
// Check result
if (result != AppOpsManager.MODE_ALLOWED) {
return new SyncNotedAppOp(result, code, attributionTag, packageName);
@@ -3281,7 +3208,7 @@
}
// As a special case for OP_CAMERA_SANDBOXED.
if (code == OP_CAMERA_SANDBOXED) {
- result = checkOperationWithState(OP_CAMERA, attributionSource.asState());
+ result = checkOperation(OP_CAMERA, uid, packageName);
// Check result
if (result != AppOpsManager.MODE_ALLOWED) {
return new SyncNotedAppOp(result, code, attributionTag, packageName);
@@ -3585,29 +3512,15 @@
packageName);
}
- /** @deprecated Use {@link #finishOperationWithState} instead. */
@Override
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag) {
- final AttributionSource attributionSource = new AttributionSource.Builder(uid)
- .setPackageName(packageName)
- .setAttributionTag(attributionTag)
- .build();
- mCheckOpsDelegateDispatcher.finishOperation(clientId, code, attributionSource);
+ mCheckOpsDelegateDispatcher.finishOperation(clientId, code, uid, packageName,
+ attributionTag);
}
- @Override
- public void finishOperationWithState(IBinder clientId, int code,
- AttributionSourceState attributionSourceState) {
- final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
- mCheckOpsDelegateDispatcher.finishOperation(clientId, code, attributionSource);
- }
-
- private void finishOperationImpl(IBinder clientId, int code,
- AttributionSource attributionSource) {
- final String packageName = attributionSource.getPackageName();
- final int uid = attributionSource.getUid();
- final String attributionTag = attributionSource.getAttributionTag();
+ private void finishOperationImpl(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -5190,13 +5103,8 @@
}
if (shell.packageName != null) {
- final AttributionSource shellAttributionSource =
- new AttributionSource.Builder(shell.packageUid)
- .setPackageName(shell.packageName)
- .setAttributionTag(shell.attributionTag)
- .build();
- shell.mInterface.startOperationWithState(shell.mToken, shell.op,
- shellAttributionSource.asState(), true, true,
+ shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
+ shell.packageName, shell.attributionTag, true, true,
"appops start shell command", true,
AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE);
} else {
@@ -5211,13 +5119,8 @@
}
if (shell.packageName != null) {
- final AttributionSource shellAttributionSource =
- new AttributionSource.Builder(shell.packageUid)
- .setPackageName(shell.packageName)
- .setAttributionTag(shell.attributionTag)
- .build();
- shell.mInterface.finishOperationWithState(shell.mToken, shell.op,
- shellAttributionSource.asState());
+ shell.mInterface.finishOperation(shell.mToken, shell.op, shell.packageUid,
+ shell.packageName, shell.attributionTag);
} else {
return -1;
}
@@ -6763,24 +6666,25 @@
return mCheckOpsDelegate;
}
- public int checkOperation(int code, AttributionSource attributionSource, boolean raw) {
+ public int checkOperation(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.checkOperation(code, attributionSource, raw,
+ return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
this::checkDelegateOperationImpl);
} else {
- return mPolicy.checkOperation(code, attributionSource, raw,
+ return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
AppOpsService.this::checkOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- return checkDelegateOperationImpl(code, attributionSource, raw);
+ return checkDelegateOperationImpl(code, uid, packageName, attributionTag, raw);
}
- return checkOperationImpl(code, attributionSource, raw);
+ return checkOperationImpl(code, uid, packageName, attributionTag, raw);
}
- private int checkDelegateOperationImpl(int code, AttributionSource attributionSource,
- boolean raw) {
- return mCheckOpsDelegate.checkOperation(code, attributionSource, raw,
+ private int checkDelegateOperationImpl(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw) {
+ return mCheckOpsDelegate.checkOperation(code, uid, packageName, attributionTag, raw,
AppOpsService.this::checkOperationImpl);
}
@@ -6805,32 +6709,32 @@
AppOpsService.this::checkAudioOperationImpl);
}
- public SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
- boolean shouldCollectAsyncNotedOp, String message,
+ public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
+ String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.noteOperation(code, attributionSource,
+ return mPolicy.noteOperation(code, uid, packageName, attributionTag,
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
this::noteDelegateOperationImpl);
} else {
- return mPolicy.noteOperation(code, attributionSource,
+ return mPolicy.noteOperation(code, uid, packageName, attributionTag,
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
AppOpsService.this::noteOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- return noteDelegateOperationImpl(code, attributionSource, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage);
+ return noteDelegateOperationImpl(code, uid, packageName,
+ attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
- return noteOperationImpl(code, attributionSource,
+ return noteOperationImpl(code, uid, packageName, attributionTag,
shouldCollectAsyncNotedOp, message, shouldCollectMessage);
}
- private SyncNotedAppOp noteDelegateOperationImpl(int code,
- AttributionSource attributionSource,
+ private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
+ @Nullable String packageName, @Nullable String featureId,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
boolean shouldCollectMessage) {
- return mCheckOpsDelegate.noteOperation(code, attributionSource,
+ return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
shouldCollectAsyncNotedOp, message, shouldCollectMessage,
AppOpsService.this::noteOperationImpl);
}
@@ -6866,38 +6770,39 @@
AppOpsService.this::noteProxyOperationImpl);
}
- public SyncNotedAppOp startOperation(IBinder token, int code,
- AttributionSource attributionSource, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, @Nullable String message,
- boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
- int attributionChainId) {
+ public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
+ @Nullable String packageName, @NonNull String attributionTag,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
+ @Nullable String message, boolean shouldCollectMessage,
+ @AttributionFlags int attributionFlags, int attributionChainId) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- return mPolicy.startOperation(token, code, attributionSource,
- startIfModeDefault, shouldCollectAsyncNotedOp, message,
+ return mPolicy.startOperation(token, code, uid, packageName,
+ attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId,
this::startDelegateOperationImpl);
} else {
- return mPolicy.startOperation(token, code, attributionSource,
+ return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId,
AppOpsService.this::startOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- return startDelegateOperationImpl(token, code, attributionSource,
+ return startDelegateOperationImpl(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, attributionFlags, attributionChainId);
}
- return startOperationImpl(token, code, attributionSource,
+ return startOperationImpl(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
attributionFlags, attributionChainId);
}
- private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code,
- AttributionSource attributionSource, boolean startIfModeDefault,
- boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
- @AttributionFlags int attributionFlags, int attributionChainId) {
- return mCheckOpsDelegate.startOperation(token, code, attributionSource,
+ private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag,
+ boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
+ boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+ int attributionChainId) {
+ return mCheckOpsDelegate.startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
attributionFlags, attributionChainId, AppOpsService.this::startOperationImpl);
}
@@ -6943,26 +6848,26 @@
attributionChainId, AppOpsService.this::startProxyOperationImpl);
}
- public void finishOperation(IBinder clientId, int code,
- AttributionSource attributionSource) {
+ public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag) {
if (mPolicy != null) {
if (mCheckOpsDelegate != null) {
- mPolicy.finishOperation(clientId, code, attributionSource,
+ mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
this::finishDelegateOperationImpl);
} else {
- mPolicy.finishOperation(clientId, code, attributionSource,
+ mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
AppOpsService.this::finishOperationImpl);
}
} else if (mCheckOpsDelegate != null) {
- finishDelegateOperationImpl(clientId, code, attributionSource);
+ finishDelegateOperationImpl(clientId, code, uid, packageName, attributionTag);
} else {
- finishOperationImpl(clientId, code, attributionSource);
+ finishOperationImpl(clientId, code, uid, packageName, attributionTag);
}
}
- private void finishDelegateOperationImpl(IBinder clientId, int code,
- AttributionSource attributionSource) {
- mCheckOpsDelegate.finishOperation(clientId, code, attributionSource,
+ private void finishDelegateOperationImpl(IBinder clientId, int code, int uid,
+ String packageName, String attributionTag) {
+ mCheckOpsDelegate.finishOperation(clientId, code, uid, packageName, attributionTag,
AppOpsService.this::finishOperationImpl);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2897075..5d4f711 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -70,7 +70,6 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -172,7 +171,7 @@
@NonNull AudioSystemAdapter audioSystem) {
mContext = context;
mAudioService = service;
- mBtHelper = new BtHelper(this);
+ mBtHelper = new BtHelper(this, context);
mDeviceInventory = new AudioDeviceInventory(this);
mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext);
mAudioSystem = audioSystem;
@@ -188,7 +187,7 @@
@NonNull AudioSystemAdapter audioSystem) {
mContext = context;
mAudioService = service;
- mBtHelper = new BtHelper(this);
+ mBtHelper = new BtHelper(this, context);
mDeviceInventory = mockDeviceInventory;
mSystemServer = mockSystemServer;
mAudioSystem = audioSystem;
@@ -1392,6 +1391,10 @@
return mAudioService.hasAudioFocusUsers();
}
+ /*package*/ void postInitSpatializerHeadTrackingSensors() {
+ mAudioService.postInitSpatializerHeadTrackingSensors();
+ }
+
//---------------------------------------------------------------------
// Message handling on behalf of helper classes.
// Each of these methods posts a message to mBrokerHandler message queue.
@@ -1475,6 +1478,15 @@
sendLMsgNoDelay(MSG_L_RECEIVED_BT_EVENT, SENDMSG_QUEUE, intent);
}
+ /*package*/ void postUpdateLeAudioGroupAddresses(int groupId) {
+ sendIMsgNoDelay(
+ MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES, SENDMSG_QUEUE, groupId);
+ }
+
+ /*package*/ void postSynchronizeLeDevicesInInventory(AdiDeviceState deviceState) {
+ sendLMsgNoDelay(MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
+ }
+
/*package*/ static final class CommunicationDeviceInfo {
final @NonNull IBinder mCb; // Identifies the requesting client for death handler
final int mUid; // Requester UID
@@ -1604,6 +1616,14 @@
}
}
+ /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) {
+ return mBtHelper.getLeAudioDeviceGroupId(device);
+ }
+
+ /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
+ return mBtHelper.getLeAudioGroupAddresses(groupId);
+ }
+
/*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent);
}
@@ -1976,6 +1996,22 @@
onCheckCommunicationRouteClientState(msg.arg1, msg.arg2 == 1);
}
} break;
+
+ case MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onUpdateLeAudioGroupAddresses(msg.arg1);
+ }
+ } break;
+
+ case MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSynchronizeLeDevicesInInventory(
+ (AdiDeviceState) msg.obj);
+ }
+ } break;
+
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -2058,6 +2094,10 @@
private static final int MSG_L_RECEIVED_BT_EVENT = 55;
private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56;
+ private static final int MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES = 57;
+ private static final int MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY = 58;
+
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
@@ -2582,9 +2622,9 @@
}
}
- @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+ List<String> getDeviceAddresses(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) {
- return mDeviceInventory.getDeviceSensorUuid(device);
+ return mDeviceInventory.getDeviceAddresses(device);
}
}
@@ -2605,15 +2645,19 @@
* in order to be mocked by a test a the same package
* (see https://code.google.com/archive/p/mockito/issues/127)
*/
- public void persistAudioDeviceSettings() {
+ public void postPersistAudioDeviceSettings() {
sendMsg(MSG_PERSIST_AUDIO_DEVICE_SETTINGS, SENDMSG_REPLACE, /*delay*/ 1000);
}
void onPersistAudioDeviceSettings() {
final String deviceSettings = mDeviceInventory.getDeviceSettings();
- Log.v(TAG, "saving AdiDeviceState: " + deviceSettings);
- final SettingsAdapter settings = mAudioService.getSettings();
- boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(),
+ Log.v(TAG, "onPersistAudioDeviceSettings AdiDeviceState: " + deviceSettings);
+ String currentSettings = readDeviceSettings();
+ if (deviceSettings.equals(currentSettings)) {
+ return;
+ }
+ final SettingsAdapter settingsAdapter = mAudioService.getSettings();
+ boolean res = settingsAdapter.putSecureStringForUser(mAudioService.getContentResolver(),
Settings.Secure.AUDIO_DEVICE_INVENTORY,
deviceSettings, UserHandle.USER_CURRENT);
if (!res) {
@@ -2621,11 +2665,17 @@
}
}
+ private String readDeviceSettings() {
+ final SettingsAdapter settingsAdapter = mAudioService.getSettings();
+ final ContentResolver contentResolver = mAudioService.getContentResolver();
+ return settingsAdapter.getSecureStringForUser(contentResolver,
+ Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
+ }
+
void onReadAudioDeviceSettings() {
final SettingsAdapter settingsAdapter = mAudioService.getSettings();
final ContentResolver contentResolver = mAudioService.getContentResolver();
- String settings = settingsAdapter.getSecureStringForUser(contentResolver,
- Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
+ String settings = readDeviceSettings();
if (settings == null) {
Log.i(TAG, "reading AdiDeviceState from legacy key"
+ Settings.Secure.SPATIAL_AUDIO_ENABLED);
@@ -2688,8 +2738,8 @@
}
@Nullable
- AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) {
- return mDeviceInventory.findBtDeviceStateForAddress(address, isBle);
+ AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) {
+ return mDeviceInventory.findBtDeviceStateForAddress(address, deviceType);
}
//------------------------------------------------
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e59fd77..7ba0827 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -15,14 +15,23 @@
*/
package com.android.server.audio;
+import static android.media.AudioSystem.DEVICE_IN_ALL_SCO_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO_SET;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
+import static android.media.AudioSystem.isBluetoothA2dpOutDevice;
import static android.media.AudioSystem.isBluetoothDevice;
+import static android.media.AudioSystem.isBluetoothLeOutDevice;
+import static android.media.AudioSystem.isBluetoothOutDevice;
+import static android.media.AudioSystem.isBluetoothScoOutDevice;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
@@ -72,7 +81,6 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.UUID;
import java.util.stream.Stream;
/**
@@ -118,6 +126,7 @@
return oldState;
});
}
+ mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState);
}
/**
@@ -125,23 +134,28 @@
* Bluetooth device and no corresponding entry already exists.
* @param ada the device to add if needed
*/
- void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
- if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
+ void addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddres) {
+ if (!isBluetoothOutDevice(deviceType)) {
return;
}
synchronized (mDeviceInventoryLock) {
- if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
+ AdiDeviceState ads = findBtDeviceStateForAddress(address, deviceType);
+ if (ads == null) {
+ ads = findBtDeviceStateForAddress(peerAddres, deviceType);
+ }
+ if (ads != null) {
+ mDeviceBroker.postSynchronizeLeDevicesInInventory(ads);
return;
}
- AdiDeviceState ads = new AdiDeviceState(
- ada.getType(), ada.getInternalType(), ada.getAddress());
+ ads = new AdiDeviceState(AudioDeviceInfo.convertInternalDeviceToDeviceType(deviceType),
+ deviceType, address);
mDeviceInventory.put(ads.getDeviceId(), ads);
+ mDeviceBroker.postPersistAudioDeviceSettings();
}
- mDeviceBroker.persistAudioDeviceSettings();
}
/**
- * Adds a new AdiDeviceState or updates the audio device cateogory of the matching
+ * Adds a new AdiDeviceState or updates the audio device category of the matching
* AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
* @param deviceState the device to update
*/
@@ -152,6 +166,63 @@
return oldState;
});
}
+ mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState);
+ }
+
+ /**
+ * synchronize AdiDeviceState for LE devices in the same group
+ */
+ void onSynchronizeLeDevicesInInventory(AdiDeviceState updatedDevice) {
+ synchronized (mDevicesLock) {
+ synchronized (mDeviceInventoryLock) {
+ boolean found = false;
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (di.mDeviceType != updatedDevice.getInternalDeviceType()) {
+ continue;
+ }
+ if (di.mDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
+ for (AdiDeviceState ads2 : mDeviceInventory.values()) {
+ if (!(di.mDeviceType == ads2.getInternalDeviceType()
+ && di.mPeerDeviceAddress.equals(ads2.getDeviceAddress()))) {
+ continue;
+ }
+ ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads2.setSAEnabled(updatedDevice.isSAEnabled());
+ ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+ found = true;
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "onSynchronizeLeDevicesInInventory synced device pair ads1="
+ + updatedDevice + " ads2=" + ads2).printLog(TAG));
+ break;
+ }
+ }
+ if (di.mPeerDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
+ for (AdiDeviceState ads2 : mDeviceInventory.values()) {
+ if (!(di.mDeviceType == ads2.getInternalDeviceType()
+ && di.mDeviceAddress.equals(ads2.getDeviceAddress()))) {
+ continue;
+ }
+ ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads2.setSAEnabled(updatedDevice.isSAEnabled());
+ ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+ found = true;
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "onSynchronizeLeDevicesInInventory synced device pair ads1="
+ + updatedDevice + " peer ads2=" + ads2).printLog(TAG));
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+ }
+ if (found) {
+ mDeviceBroker.postPersistAudioDeviceSettings();
+ }
+ }
+ }
}
/**
@@ -163,9 +234,21 @@
* @return the found {@link AdiDeviceState} or {@code null} otherwise.
*/
@Nullable
- AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) {
+ AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) {
+ Set<Integer> deviceSet;
+ if (isBluetoothA2dpOutDevice(deviceType)) {
+ deviceSet = DEVICE_OUT_ALL_A2DP_SET;
+ } else if (isBluetoothLeOutDevice(deviceType)) {
+ deviceSet = DEVICE_OUT_ALL_BLE_SET;
+ } else if (isBluetoothScoOutDevice(deviceType)) {
+ deviceSet = DEVICE_OUT_ALL_SCO_SET;
+ } else if (deviceType == DEVICE_OUT_HEARING_AID) {
+ deviceSet = new HashSet<>();
+ deviceSet.add(DEVICE_OUT_HEARING_AID);
+ } else {
+ return null;
+ }
synchronized (mDeviceInventoryLock) {
- final Set<Integer> deviceSet = isBle ? DEVICE_OUT_ALL_BLE_SET : DEVICE_OUT_ALL_A2DP_SET;
for (Integer internalType : deviceSet) {
AdiDeviceState deviceState = mDeviceInventory.get(
new Pair<>(internalType, address));
@@ -345,7 +428,8 @@
final @NonNull String mDeviceName;
final @NonNull String mDeviceAddress;
int mDeviceCodecFormat;
- final UUID mSensorUuid;
+ @NonNull String mPeerDeviceAddress;
+ final int mGroupId;
/** Disabled operating modes for this device. Use a negative logic so that by default
* an empty list means all modes are allowed.
@@ -353,12 +437,13 @@
@NonNull ArraySet<String> mDisabledModes = new ArraySet(0);
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat, @Nullable UUID sensorUuid) {
+ int deviceCodecFormat, String peerDeviceAddress, int groupId) {
mDeviceType = deviceType;
mDeviceName = deviceName == null ? "" : deviceName;
mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
mDeviceCodecFormat = deviceCodecFormat;
- mSensorUuid = sensorUuid;
+ mPeerDeviceAddress = peerDeviceAddress == null ? "" : peerDeviceAddress;
+ mGroupId = groupId;
}
void setModeDisabled(String mode) {
@@ -379,7 +464,8 @@
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
int deviceCodecFormat) {
- this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
+ this(deviceType, deviceName, deviceAddress, deviceCodecFormat,
+ null, BluetoothLeAudio.GROUP_ID_INVALID);
}
DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
@@ -393,7 +479,8 @@
+ ") name:" + mDeviceName
+ " addr:" + mDeviceAddress
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
- + " sensorUuid: " + Objects.toString(mSensorUuid)
+ + " peer addr:" + mPeerDeviceAddress
+ + " group:" + mGroupId
+ " disabled modes: " + mDisabledModes + "]";
}
@@ -714,6 +801,27 @@
}
}
+
+ /*package*/ void onUpdateLeAudioGroupAddresses(int groupId) {
+ synchronized (mDevicesLock) {
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (di.mGroupId == groupId) {
+ List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ if (di.mPeerDeviceAddress.equals("")) {
+ for (String addr : addresses) {
+ if (!addr.equals(di.mDeviceAddress)) {
+ di.mPeerDeviceAddress = addr;
+ break;
+ }
+ }
+ } else if (!addresses.contains(di.mPeerDeviceAddress)) {
+ di.mPeerDeviceAddress = "";
+ }
+ }
+ }
+ }
+ }
+
/*package*/ void onReportNewRoutes() {
int n = mRoutesObservers.beginBroadcast();
if (n > 0) {
@@ -1419,7 +1527,7 @@
if (!connect) {
purgeDevicesRoles_l();
} else {
- addAudioDeviceInInventoryIfNeeded(attributes);
+ addAudioDeviceInInventoryIfNeeded(device, address, "");
}
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
@@ -1477,7 +1585,7 @@
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_HEARING_AID devices
mConnectedDevices.values().forEach(deviceInfo -> {
- if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
+ if (deviceInfo.mDeviceType == DEVICE_OUT_HEARING_AID) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
@@ -1485,8 +1593,8 @@
.set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
.record();
if (toRemove.size() > 0) {
- final int delay = checkSendBecomingNoisyIntentInt(
- AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE);
+ final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
+ AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
// TODO delay not used?
makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/)
@@ -1687,12 +1795,8 @@
// Reset A2DP suspend state each time a new sink is connected
mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
- // The convention for head tracking sensors associated with A2DP devices is to
- // use a UUID derived from the MAC address as follows:
- // time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
- UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, codec, sensorUuid);
+ address, codec);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -1703,7 +1807,7 @@
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
- addAudioDeviceInInventoryIfNeeded(ada);
+ addAudioDeviceInInventoryIfNeeded(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, "");
}
static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER,
@@ -1723,15 +1827,15 @@
return;
}
DeviceInfo leOutDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_BLE_SET);
DeviceInfo leInDevice =
getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET);
DeviceInfo a2dpDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET);
DeviceInfo scoOutDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET);
DeviceInfo scoInDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET);
boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled());
boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled())
|| (leInDevice != null && leInDevice.isDuplexModeEnabled());
@@ -1765,7 +1869,7 @@
continue;
}
- if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) {
+ if (isBluetoothOutDevice(di.mDeviceType)) {
for (AudioProductStrategy strategy : mStrategies) {
boolean disable = false;
if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) {
@@ -1832,23 +1936,20 @@
int checkProfileIsConnected(int profile) {
switch (profile) {
case BluetoothProfile.HEADSET:
- if (getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null
- || getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) {
+ if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET) != null
+ || getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET) != null) {
return profile;
}
break;
case BluetoothProfile.A2DP:
- if (getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) {
+ if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET) != null) {
return profile;
}
break;
case BluetoothProfile.LE_AUDIO:
case BluetoothProfile.LE_AUDIO_BROADCAST:
if (getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null
+ DEVICE_OUT_ALL_BLE_SET) != null
|| getFirstConnectedDeviceOfTypes(
AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) {
return profile;
@@ -2006,28 +2107,28 @@
private void makeHearingAidDeviceAvailable(
String address, String name, int streamType, String eventSource) {
final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
- AudioSystem.DEVICE_OUT_HEARING_AID);
+ DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
AudioDeviceAttributes ada = new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
+ DEVICE_OUT_HEARING_AID, address, name);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
- DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
- new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address));
- mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
+ DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address),
+ new DeviceInfo(DEVICE_OUT_HEARING_AID, name, address));
+ mDeviceBroker.postAccessoryPlugMediaUnmute(DEVICE_OUT_HEARING_AID);
mDeviceBroker.postApplyVolumeOnDevice(streamType,
- AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
+ DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
- addAudioDeviceInInventoryIfNeeded(ada);
+ addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_HEARING_AID, address, "");
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID))
+ AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID))
.set(MediaMetrics.Property.NAME, name)
.set(MediaMetrics.Property.STREAM_TYPE,
AudioSystem.streamToString(streamType))
@@ -2037,18 +2138,18 @@
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
AudioDeviceAttributes ada = new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address);
+ DEVICE_OUT_HEARING_AID, address);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
- DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
+ DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address));
// Remove Hearing Aid routes as well
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceUnavailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID))
+ AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID))
.record();
mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
@@ -2060,7 +2161,7 @@
*/
boolean isHearingAidConnected() {
return getFirstConnectedDeviceOfTypes(
- Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null;
+ Sets.newHashSet(DEVICE_OUT_HEARING_AID)) != null;
}
/**
@@ -2102,6 +2203,20 @@
final String address = btInfo.mDevice.getAddress();
String name = BtHelper.getName(btInfo.mDevice);
+ // Find LE Group ID and peer headset address if available
+ final int groupId = mDeviceBroker.getLeAudioDeviceGroupId(btInfo.mDevice);
+ String peerAddress = "";
+ if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
+ List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ if (addresses.size() > 1) {
+ for (String addr : addresses) {
+ if (!addr.equals(address)) {
+ peerAddress = addr;
+ break;
+ }
+ }
+ }
+ }
// The BT Stack does not provide a name for LE Broadcast devices
if (device == AudioSystem.DEVICE_OUT_BLE_BROADCAST && name.equals("")) {
name = "Broadcast";
@@ -2127,14 +2242,12 @@
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
-
- UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT,
- sensorUuid));
+ peerAddress, groupId));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
- addAudioDeviceInInventoryIfNeeded(ada);
+ addAudioDeviceInInventoryIfNeeded(device, address, peerAddress);
}
if (streamType == AudioSystem.STREAM_DEFAULT) {
@@ -2226,12 +2339,12 @@
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HDMI);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE);
- BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
+ BECOMING_NOISY_INTENT_DEVICES_SET.add(DEVICE_OUT_HEARING_AID);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
- BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
+ BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_A2DP_SET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
- BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
+ BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_BLE_SET);
}
// must be called before removing the device from mConnectedDevices
@@ -2512,16 +2625,22 @@
mDevRoleCapturePresetDispatchers.finishBroadcast();
}
- @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+ List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ List<String> addresses = new ArrayList<String>();
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
synchronized (mDevicesLock) {
DeviceInfo di = mConnectedDevices.get(key);
- if (di == null) {
- return null;
+ if (di != null) {
+ if (!di.mDeviceAddress.isEmpty()) {
+ addresses.add(di.mDeviceAddress);
+ }
+ if (!di.mPeerDeviceAddress.isEmpty()) {
+ addresses.add(di.mPeerDeviceAddress);
+ }
}
- return di.mSensorUuid;
}
+ return addresses;
}
/*package*/ String getDeviceSettings() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b209fb0..99321c4 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -38,6 +38,7 @@
import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
import static com.android.media.audio.Flags.bluetoothMacAddressAnonymization;
+import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE;
import static com.android.server.utils.EventLogger.Event.ALOGE;
import static com.android.server.utils.EventLogger.Event.ALOGI;
@@ -236,7 +237,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
-import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -1371,19 +1371,21 @@
sRingerAndZenModeMutedStreams, "onInitStreamsAndVolumes"));
setRingerModeInt(getRingerModeInternal(), false);
- final float[] preScale = new float[3];
- preScale[0] = mContext.getResources().getFraction(
- com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
- 1, 1);
- preScale[1] = mContext.getResources().getFraction(
- com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
- 1, 1);
- preScale[2] = mContext.getResources().getFraction(
- com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
- 1, 1);
- for (int i = 0; i < preScale.length; i++) {
- if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
- mPrescaleAbsoluteVolume[i] = preScale[i];
+ if (!disablePrescaleAbsoluteVolume()) {
+ final float[] preScale = new float[3];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+ for (int i = 0; i < preScale.length; i++) {
+ if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
+ mPrescaleAbsoluteVolume[i] = preScale[i];
+ }
}
}
@@ -8618,7 +8620,7 @@
if (index == 0) {
// 0% for volume 0
index = 0;
- } else if (index > 0 && index <= 3) {
+ } else if (!disablePrescaleAbsoluteVolume() && index > 0 && index <= 3) {
// Pre-scale for volume steps 1 2 and 3
index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10;
} else {
@@ -11051,7 +11053,9 @@
final String addr = Objects.requireNonNull(address);
- AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr, isBle);
+ AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr,
+ (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET
+ : AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
int internalType = !isBle ? DEVICE_OUT_BLUETOOTH_A2DP
: ((btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES)
@@ -11067,7 +11071,7 @@
deviceState.setAudioDeviceCategory(btAudioDeviceCategory);
mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes());
mSoundDoseHelper.setAudioDeviceCategory(addr, internalType,
@@ -11081,7 +11085,8 @@
super.getBluetoothAudioDeviceCategory_enforcePermission();
final AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(
- Objects.requireNonNull(address), isBle);
+ Objects.requireNonNull(address), (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET
+ : AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
if (deviceState == null) {
return AUDIO_DEVICE_CATEGORY_UNKNOWN;
}
@@ -11448,6 +11453,14 @@
pw.print(" adjust-only absolute volume devices="); pw.println(dumpDeviceTypes(
getAbsoluteVolumeDevicesWithBehavior(
AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY)));
+ pw.print(" pre-scale for bluetooth absolute volume ");
+ if (disablePrescaleAbsoluteVolume()) {
+ pw.println("= disabled");
+ } else {
+ pw.println("=" + mPrescaleAbsoluteVolume[0]
+ + ", " + mPrescaleAbsoluteVolume[1]
+ + ", " + mPrescaleAbsoluteVolume[2]);
+ }
pw.print(" mExtVolumeController="); pw.println(mExtVolumeController);
pw.print(" mHdmiAudioSystemClient="); pw.println(mHdmiAudioSystemClient);
pw.print(" mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
@@ -13585,8 +13598,8 @@
return activeAssistantUids;
}
- UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
- return mDeviceBroker.getDeviceSensorUuid(device);
+ List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ return mDeviceBroker.getDeviceAddresses(device);
}
//======================
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index cce6bd2..7b96215 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -26,7 +26,9 @@
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothProfile;
+import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
@@ -43,6 +45,7 @@
import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -57,9 +60,11 @@
private static final String TAG = "AS.BtHelper";
private final @NonNull AudioDeviceBroker mDeviceBroker;
+ private final @NonNull Context mContext;
- BtHelper(@NonNull AudioDeviceBroker broker) {
+ BtHelper(@NonNull AudioDeviceBroker broker, Context context) {
mDeviceBroker = broker;
+ mContext = context;
}
// BluetoothHeadset API to control SCO connection
@@ -498,6 +503,32 @@
}
}
+ // BluetoothLeAudio callback used to update the list of addresses in the same group as a
+ // connected LE Audio device
+ MyLeAudioCallback mLeAudioCallback = null;
+
+ class MyLeAudioCallback implements BluetoothLeAudio.Callback {
+ @Override
+ public void onCodecConfigChanged(int groupId,
+ @NonNull BluetoothLeAudioCodecStatus status) {
+ // Do nothing
+ }
+
+ @Override
+ public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) {
+ mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
+ }
+
+ @Override
+ public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) {
+ mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
+ }
+ @Override
+ public void onGroupStatusChanged(int groupId, int groupStatus) {
+ mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
+ }
+ }
+
// @GuardedBy("mDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
@@ -519,6 +550,11 @@
mHearingAid = (BluetoothHearingAid) proxy;
break;
case BluetoothProfile.LE_AUDIO:
+ if (mLeAudio == null) {
+ mLeAudioCallback = new MyLeAudioCallback();
+ ((BluetoothLeAudio) proxy).registerCallback(
+ mContext.getMainExecutor(), mLeAudioCallback);
+ }
mLeAudio = (BluetoothLeAudio) proxy;
break;
case BluetoothProfile.A2DP_SINK:
@@ -977,6 +1013,28 @@
return result;
}
+ /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) {
+ if (mLeAudio == null || device == null) {
+ return BluetoothLeAudio.GROUP_ID_INVALID;
+ }
+ return mLeAudio.getGroupId(device);
+ }
+
+ /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
+ List<String> addresses = new ArrayList<String>();
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter == null || mLeAudio == null) {
+ return addresses;
+ }
+ List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
+ for (BluetoothDevice device : activeDevices) {
+ if (device != null && mLeAudio.getGroupId(device) == groupId) {
+ addresses.add(device.getAddress());
+ }
+ }
+ return addresses;
+ }
+
/**
* Returns the String equivalent of the btCodecType.
*
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 7abd9c7..ea92154 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -564,7 +564,7 @@
}
if (updatedDevice != null) {
onRoutingUpdated();
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(updatedDevice, "addCompatibleAudioDevice");
}
}
@@ -614,7 +614,7 @@
if (deviceState != null && deviceState.isSAEnabled()) {
deviceState.setSAEnabled(false);
onRoutingUpdated();
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "removeCompatibleAudioDevice");
}
}
@@ -716,7 +716,7 @@
ada.getAddress());
initSAState(deviceState);
mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
}
}
@@ -1206,7 +1206,7 @@
}
Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
deviceState.setHeadTrackerEnabled(enabled);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "setHeadTrackerEnabled");
// check current routing to see if it affects the headtracking mode
@@ -1248,7 +1248,7 @@
if (deviceState != null) {
if (!deviceState.hasHeadTracker()) {
deviceState.setHasHeadTracker(true);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "setHasHeadTracker");
}
return deviceState.isHeadTrackerEnabled();
@@ -1631,25 +1631,33 @@
return headHandle;
}
final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
- UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(currentDevice);
+ List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice);
+
// We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion
// with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR
// and Sensor.TYPE_GAME_ROTATION_VECTOR are supported internally by
// SensorPoseProvider).
// Note: this is a dynamic sensor list right now.
List<Sensor> sensors = mSensorManager.getDynamicSensorList(Sensor.TYPE_HEAD_TRACKER);
- for (Sensor sensor : sensors) {
- final UUID uuid = sensor.getUuid();
- if (uuid.equals(routingDeviceUuid)) {
- headHandle = sensor.getHandle();
- if (!setHasHeadTracker(currentDevice)) {
- headHandle = -1;
+ for (String address : deviceAddresses) {
+ UUID routingDeviceUuid = UuidUtils.uuidFromAudioDeviceAttributes(
+ new AudioDeviceAttributes(currentDevice.getInternalType(), address));
+ for (Sensor sensor : sensors) {
+ final UUID uuid = sensor.getUuid();
+ if (uuid.equals(routingDeviceUuid)) {
+ headHandle = sensor.getHandle();
+ if (!setHasHeadTracker(currentDevice)) {
+ headHandle = -1;
+ }
+ break;
}
- break;
+ if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
+ headHandle = sensor.getHandle();
+ // we do not break, perhaps we find a head tracker on device.
+ }
}
- if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
- headHandle = sensor.getHandle();
- // we do not break, perhaps we find a head tracker on device.
+ if (headHandle != -1) {
+ break;
}
}
return headHandle;
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index ab480e8..0814375 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -31,6 +31,7 @@
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
import java.io.File;
@@ -141,6 +142,13 @@
return System.currentTimeMillis();
}
+ /** Logs when system server started. */
+ public void logSystemServerStarted() {
+ CriticalEventProto event = new CriticalEventProto();
+ event.setSystemServerStarted(new SystemServerStarted());
+ log(event);
+ }
+
/** Logs a watchdog. */
public void logWatchdog(String subject, UUID uuid) {
Watchdog watchdog = new Watchdog();
diff --git a/services/core/java/com/android/server/media/BluetoothProfileMonitor.java b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
index b129dd0..d61e7fb 100644
--- a/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
+++ b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
@@ -47,21 +47,10 @@
@Nullable
private BluetoothLeAudio mLeAudioProfile;
- @Nullable
- private OnProfileChangedListener mOnProfileChangedListener;
-
BluetoothProfileMonitor(@NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter) {
- Objects.requireNonNull(context);
- Objects.requireNonNull(bluetoothAdapter);
-
- mContext = context;
- mBluetoothAdapter = bluetoothAdapter;
- }
-
- /* package */ synchronized void setOnProfileChangedListener(
- @NonNull OnProfileChangedListener listener) {
- mOnProfileChangedListener = listener;
+ mContext = Objects.requireNonNull(context);
+ mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
}
/* package */ void start() {
@@ -115,15 +104,9 @@
}
}
- /* package */ interface OnProfileChangedListener {
- void onProfileChange(int profile);
- }
-
private final class ProfileListener implements BluetoothProfile.ServiceListener {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- OnProfileChangedListener listener;
-
synchronized (BluetoothProfileMonitor.this) {
switch (profile) {
case BluetoothProfile.A2DP:
@@ -135,22 +118,12 @@
case BluetoothProfile.LE_AUDIO:
mLeAudioProfile = (BluetoothLeAudio) proxy;
break;
- default:
- return;
}
-
- listener = mOnProfileChangedListener;
- }
-
- if (listener != null) {
- listener.onProfileChange(profile);
}
}
@Override
public void onServiceDisconnected(int profile) {
- OnProfileChangedListener listener;
-
synchronized (BluetoothProfileMonitor.this) {
switch (profile) {
case BluetoothProfile.A2DP:
@@ -162,17 +135,8 @@
case BluetoothProfile.LE_AUDIO:
mLeAudioProfile = null;
break;
- default:
- return;
}
-
- listener = mOnProfileChangedListener;
- }
-
- if (listener != null) {
- listener.onProfileChange(profile);
}
}
}
-
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 71562dc..9802adf 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -26,6 +26,7 @@
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
+import android.app.Flags;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
@@ -670,14 +671,37 @@
if (rule.enabled != automaticZenRule.isEnabled()) {
rule.snoozing = false;
}
+ if (Flags.modesApi()) {
+ rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed();
+ rule.iconResId = automaticZenRule.getIconResId();
+ rule.triggerDescription = automaticZenRule.getTriggerDescription();
+ rule.type = automaticZenRule.getType();
+ }
}
protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
- AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
- rule.configurationActivity,
- rule.conditionId, rule.zenPolicy,
- NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
- rule.enabled, rule.creationTime);
+ AutomaticZenRule azr;
+ if (Flags.modesApi()) {
+ azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId)
+ .setManualInvocationAllowed(rule.allowManualInvocation)
+ .setCreationTime(rule.creationTime)
+ .setIconResId(rule.iconResId)
+ .setType(rule.type)
+ .setZenPolicy(rule.zenPolicy)
+ .setEnabled(rule.enabled)
+ .setInterruptionFilter(
+ NotificationManager.zenModeToInterruptionFilter(rule.zenMode))
+ .setOwner(rule.component)
+ .setConfigurationActivity(rule.configurationActivity)
+ .setTriggerDescription(rule.triggerDescription)
+ .build();
+ } else {
+ azr = new AutomaticZenRule(rule.name, rule.component,
+ rule.configurationActivity,
+ rule.conditionId, rule.zenPolicy,
+ NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
+ rule.enabled, rule.creationTime);
+ }
azr.setPackageName(rule.pkg);
return azr;
}
@@ -713,6 +737,9 @@
newRule.zenMode = zenMode;
newRule.conditionId = conditionId;
newRule.enabler = caller;
+ if (Flags.modesApi()) {
+ newRule.allowManualInvocation = true;
+ }
newConfig.manualRule = newRule;
}
setConfigLocked(newConfig, reason, null, setRingerMode, callingUid,
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index b006ac8..a8cba53 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -35,11 +35,12 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
import libcore.io.IoUtils;
@@ -53,6 +54,9 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@@ -114,21 +118,26 @@
private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
- private static final int HEADER_SIZE = 8;
+ @VisibleForTesting
+ static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
private static final int PARTITION_TYPE_MARKER = 0x19901873;
/** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
/** Maximum size of the FRP credential handle that can be stored. */
- private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
+ @VisibleForTesting
+ static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
/**
* Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
*/
private static final int TEST_MODE_RESERVED_SIZE = 10000;
/** Maximum size of the Test Harness Mode data that can be stored. */
- private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
+ @VisibleForTesting
+ static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
+
// Limit to 100k as blocks larger than this might cause strain on Binder.
- private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
+ @VisibleForTesting
+ static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
public static final int DIGEST_SIZE_BYTES = 32;
private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
@@ -138,12 +147,12 @@
private final Context mContext;
private final String mDataBlockFile;
- private final boolean mIsRunningDSU;
+ private final boolean mIsFileBacked;
private final Object mLock = new Object();
private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
private int mAllowedUid = -1;
- private long mBlockDeviceSize;
+ private long mBlockDeviceSize = -1; // Load lazily
@GuardedBy("mLock")
private boolean mIsWritable = true;
@@ -151,13 +160,23 @@
public PersistentDataBlockService(Context context) {
super(context);
mContext = context;
- mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
- if (mIsRunningDSU) {
+ if (SystemProperties.getBoolean(GSI_RUNNING_PROP, false)) {
+ mIsFileBacked = true;
mDataBlockFile = GSI_SANDBOX;
} else {
+ mIsFileBacked = false;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
}
- mBlockDeviceSize = -1; // Load lazily
+ }
+
+ @VisibleForTesting
+ PersistentDataBlockService(Context context, boolean isFileBacked, String dataBlockFile,
+ long blockDeviceSize) {
+ super(context);
+ mContext = context;
+ mIsFileBacked = isFileBacked;
+ mDataBlockFile = dataBlockFile;
+ mBlockDeviceSize = blockDeviceSize;
}
private int getAllowedUid() {
@@ -212,6 +231,11 @@
super.onBootPhase(phase);
}
+ @VisibleForTesting
+ void setAllowedUid(int uid) {
+ mAllowedUid = uid;
+ }
+
private void formatIfOemUnlockEnabled() {
boolean enabled = doGetOemUnlockEnabled();
if (enabled) {
@@ -220,7 +244,7 @@
}
}
- SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
+ setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0");
}
private void enforceOemUnlockReadPermission() {
@@ -278,7 +302,7 @@
private long getBlockDeviceSize() {
synchronized (mLock) {
if (mBlockDeviceSize == -1) {
- if (mIsRunningDSU) {
+ if (mIsFileBacked) {
mBlockDeviceSize = MAX_DATA_BLOCK_SIZE;
} else {
mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
@@ -289,14 +313,26 @@
return mBlockDeviceSize;
}
- private long getFrpCredentialDataOffset() {
- return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
+ @VisibleForTesting
+ int getMaximumFrpDataSize() {
+ return (int) (getTestHarnessModeDataOffset() - DIGEST_SIZE_BYTES - HEADER_SIZE);
}
- private long getTestHarnessModeDataOffset() {
+ @VisibleForTesting
+ long getFrpCredentialDataOffset() {
+ return getOemUnlockDataOffset() - FRP_CREDENTIAL_RESERVED_SIZE;
+ }
+
+ @VisibleForTesting
+ long getTestHarnessModeDataOffset() {
return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
}
+ @VisibleForTesting
+ long getOemUnlockDataOffset() {
+ return getBlockDeviceSize() - 1;
+ }
+
private boolean enforceChecksumValidity() {
byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
@@ -385,7 +421,8 @@
return md.digest();
}
- private void formatPartitionLocked(boolean setOemUnlockEnabled) {
+ @VisibleForTesting
+ void formatPartitionLocked(boolean setOemUnlockEnabled) {
try {
FileChannel channel = getBlockOutputChannel();
@@ -448,10 +485,15 @@
Slog.e(TAG, "unable to access persistent partition", e);
return;
} finally {
- SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
+ setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0");
}
}
+ @VisibleForTesting
+ void setProperty(String name, String value) {
+ SystemProperties.set(name, value);
+ }
+
private boolean doGetOemUnlockEnabled() {
DataInputStream inputStream;
try {
@@ -483,6 +525,16 @@
private native long nativeGetBlockDeviceSize(String path);
private native int nativeWipe(String path);
+ @VisibleForTesting
+ IPersistentDataBlockService getInterfaceForTesting() {
+ return IPersistentDataBlockService.Stub.asInterface(mService);
+ }
+
+ @VisibleForTesting
+ PersistentDataBlockManagerInternal getInternalInterfaceForTesting() {
+ return mInternalService;
+ }
+
private final IBinder mService = new IPersistentDataBlockService.Stub() {
/**
@@ -588,7 +640,18 @@
enforceOemUnlockWritePermission();
synchronized (mLock) {
- int ret = nativeWipe(mDataBlockFile);
+ int ret;
+ if (mIsFileBacked) {
+ try {
+ Files.write(Paths.get(mDataBlockFile), new byte[MAX_DATA_BLOCK_SIZE],
+ StandardOpenOption.TRUNCATE_EXISTING);
+ ret = 0;
+ } catch (IOException e) {
+ ret = -1;
+ }
+ } else {
+ ret = nativeWipe(mDataBlockFile);
+ }
if (ret < 0) {
Slog.e(TAG, "failed to wipe persistent partition");
@@ -699,7 +762,7 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
pw.println("mDataBlockFile: " + mDataBlockFile);
- pw.println("mIsRunningDSU: " + mIsRunningDSU);
+ pw.println("mIsFileBacked: " + mIsFileBacked);
pw.println("mInitDoneSignal: " + mInitDoneSignal);
pw.println("mAllowedUid: " + mAllowedUid);
pw.println("mBlockDeviceSize: " + mBlockDeviceSize);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 26e70c0..21284a0 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -410,7 +410,7 @@
* Map of all apex system services to the jar files they are contained in.
*/
@GuardedBy("mLock")
- private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
+ private final List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
/**
* Contains the list of {@code packageName}s of apks-in-apex for given
@@ -418,14 +418,14 @@
* difference between {@code packageName} and {@code apexModuleName}.
*/
@GuardedBy("mLock")
- private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
+ private final ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
/**
* Contains the list of {@code Exception}s that were raised when installing apk-in-apex
* inside {@code apexModuleName}.
*/
@GuardedBy("mLock")
- private Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
+ private final Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
/**
* An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index 5b32a94..a5bc2c3 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -201,7 +201,7 @@
protected static final boolean CACHE_VALID = true;
protected static final boolean CACHE_INVALID = false;
- protected AtomicBoolean mCacheValid = new AtomicBoolean(CACHE_INVALID);
+ protected final AtomicBoolean mCacheValid = new AtomicBoolean(CACHE_INVALID);
protected boolean isForceQueryable(int callingAppId) {
return mForceQueryable.contains(callingAppId);
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 9bca9f0..36677df 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -89,7 +89,7 @@
private static final long CANCELLATION_WAIT_CHECK_INTERVAL_MS = 200;
- private static ComponentName sDexoptServiceName =
+ private static final ComponentName sDexoptServiceName =
new ComponentName("android", BackgroundDexOptJobService.class.getName());
// Possible return codes of individual optimization steps.
@@ -179,7 +179,7 @@
private final long mDowngradeUnusedAppsThresholdInMillis;
- private List<PackagesUpdatedListener> mPackagesUpdatedListeners = new ArrayList<>();
+ private final List<PackagesUpdatedListener> mPackagesUpdatedListeners = new ArrayList<>();
private int mThermalStatusCutoff = THERMAL_CUTOFF_DEFAULT;
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index e367609..7b35589 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -100,6 +100,22 @@
mAppsFilter = injector.getAppsFilter();
}
+ /**
+ * Sends a broadcast to registered clients on userId for the given Intent.
+ */
+ void sendPackageBroadcastWithIntent(Intent intent, int userId, boolean isInstantApp,
+ @Intent.Flags int flags,
+ int[] visibilityAllowList,
+ final IIntentReceiver finishedReceiver,
+ @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+ @Nullable Bundle bOptions) {
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
+ SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+ broadcastAllowList.put(userId, visibilityAllowList);
+ broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
+ filterExtrasForReceiver, bOptions);
+ }
+
void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds, int[] instantUserIds,
@@ -152,8 +168,6 @@
for (int userId : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
- final String[] requiredPermissions =
- isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
if (extras != null) {
intent.putExtras(extras);
}
@@ -172,30 +186,41 @@
}
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
- if (DEBUG_BROADCASTS) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "Sending to user " + userId + ": "
- + intent.toShortString(false, true, false, false)
- + " " + intent.getExtras(), here);
- }
- final boolean ordered;
- if (mAmInternal.isModernQueueEnabled()) {
- // When the modern broadcast stack is enabled, deliver all our
- // broadcasts as unordered, since the modern stack has better
- // support for sequencing cold-starts, and it supports
- // delivering resultTo for non-ordered broadcasts
- ordered = false;
- } else {
- ordered = (finishedReceiver != null);
- }
- mAmInternal.broadcastIntent(
- intent, finishedReceiver, requiredPermissions, ordered, userId,
- broadcastAllowList == null ? null : broadcastAllowList.get(userId),
+ broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
filterExtrasForReceiver, bOptions);
}
}
+
+ private void broadcastIntent(Intent intent, IIntentReceiver finishedReceiver,
+ boolean isInstantApp, int userId, @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+ @Nullable Bundle bOptions) {
+ final String[] requiredPermissions =
+ isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
+ if (DEBUG_BROADCASTS) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "Sending to user " + userId + ": "
+ + intent.toShortString(false, true, false, false)
+ + " " + intent.getExtras(), here);
+ }
+ final boolean ordered;
+ if (mAmInternal.isModernQueueEnabled()) {
+ // When the modern broadcast stack is enabled, deliver all our
+ // broadcasts as unordered, since the modern stack has better
+ // support for sequencing cold-starts, and it supports
+ // delivering resultTo for non-ordered broadcasts
+ ordered = false;
+ } else {
+ ordered = (finishedReceiver != null);
+ }
+ mAmInternal.broadcastIntent(
+ intent, finishedReceiver, requiredPermissions, ordered, userId,
+ broadcastAllowList == null ? null : broadcastAllowList.get(userId),
+ filterExtrasForReceiver, bOptions);
+ }
+
void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
boolean mediaStatus,
boolean replacing,
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/CrossProfileAppsService.java
index 486282a..0bd165e 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsService.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsService.java
@@ -21,7 +21,7 @@
import com.android.server.SystemService;
public class CrossProfileAppsService extends SystemService {
- private CrossProfileAppsServiceImpl mServiceImpl;
+ private final CrossProfileAppsServiceImpl mServiceImpl;
public CrossProfileAppsService(Context context) {
super(context);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 5f28e56..f1dc284 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -78,8 +78,8 @@
private final LocalService mLocalService = new LocalService();
- private Context mContext;
- private Injector mInjector;
+ private final Context mContext;
+ private final Injector mInjector;
public CrossProfileAppsServiceImpl(Context context) {
this(context, new InjectorImpl(context));
@@ -783,7 +783,7 @@
}
private static class InjectorImpl implements Injector {
- private Context mContext;
+ private final Context mContext;
public InjectorImpl(Context context) {
mContext = context;
diff --git a/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java b/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
index 72f3afc..3313e72 100644
--- a/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
+++ b/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
@@ -22,10 +22,10 @@
public final class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
- ResolveInfo mResolveInfo;
+ final ResolveInfo mResolveInfo;
int mHighestApprovalLevel;
@UserIdInt
- int mTargetUserId = UserHandle.USER_CURRENT; // default as current user
+ final int mTargetUserId;
CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel, @UserIdInt
int targetUserId) {
@@ -37,6 +37,7 @@
CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel) {
this.mResolveInfo = resolveInfo;
this.mHighestApprovalLevel = highestApprovalLevel;
+ this.mTargetUserId = UserHandle.USER_CURRENT; // default as current user
}
@Override
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java
index 51214fa..56e9c0a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java
@@ -25,11 +25,11 @@
* Helper class to manage {@link com.android.server.pm.CrossProfileIntentFilter}s.
*/
public class CrossProfileIntentFilterHelper {
- private Context mContext;
- private UserManagerInternal mUserManagerInternal;
- private Settings mSettings;
- private UserManagerService mUserManagerService;
- private PackageManagerTracedLock mLock;
+ private final Context mContext;
+ private final UserManagerInternal mUserManagerInternal;
+ private final Settings mSettings;
+ private final UserManagerService mUserManagerService;
+ private final PackageManagerTracedLock mLock;
public CrossProfileIntentFilterHelper(Settings settings, UserManagerService userManagerService,
PackageManagerTracedLock lock, UserManagerInternal userManagerInternal,
diff --git a/services/core/java/com/android/server/pm/CrossProfileResolver.java b/services/core/java/com/android/server/pm/CrossProfileResolver.java
index a8da818..1d38bbb 100644
--- a/services/core/java/com/android/server/pm/CrossProfileResolver.java
+++ b/services/core/java/com/android/server/pm/CrossProfileResolver.java
@@ -36,8 +36,8 @@
*/
public abstract class CrossProfileResolver {
- protected ComponentResolverApi mComponentResolver;
- protected UserManagerService mUserManager;
+ protected final ComponentResolverApi mComponentResolver;
+ protected final UserManagerService mUserManager;
public CrossProfileResolver(ComponentResolverApi componentResolver,
UserManagerService userManager) {
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 29322e2..888e1c2 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -50,7 +50,8 @@
private final HandlerThread mThread;
private final Handler mHandler;
private final DataLoaderManagerBinderService mBinderService;
- private SparseArray<DataLoaderServiceConnection> mServiceConnections = new SparseArray<>();
+ private final SparseArray<DataLoaderServiceConnection> mServiceConnections =
+ new SparseArray<>();
public DataLoaderManagerService(Context context) {
super(context);
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
index dc5915d..db05dd3 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -75,10 +75,10 @@
}
static final class Builder {
- private WatchedIntentFilter mFilter = new WatchedIntentFilter();
- private int mFlags;
- private @Direction int mDirection;
- private boolean mLetsPersonalDataIntoProfile;
+ private final WatchedIntentFilter mFilter = new WatchedIntentFilter();
+ private final int mFlags;
+ private final @Direction int mDirection;
+ private final boolean mLetsPersonalDataIntoProfile;
Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
mDirection = direction;
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index dd96a2b..cc6bb00 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -67,7 +67,8 @@
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
// if we move dex files under the common app path.
- @Nullable String[] mInstructionSets;
+ @Nullable
+ final String[] mInstructionSets;
InstallArgs(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
int installFlags, int developmentInstallFlags, InstallSource installSource,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index a52870e..f8d27f1 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -476,20 +476,30 @@
pkgSetting.setLoadingProgress(1f);
}
+ // TODO: passes the package name as an argument in a message to the handler for V+
+ // so we don't need to rely on creating lambda objects so frequently.
+ if (UpdateOwnershipHelper.hasValidOwnershipDenyList(pkgSetting)) {
+ mPm.mHandler.post(() -> handleUpdateOwnerDenyList(pkgSetting));
+ }
+ return pkg;
+ }
+
+ private void handleUpdateOwnerDenyList(PackageSetting pkgSetting) {
ArraySet<String> listItems = mUpdateOwnershipHelper.readUpdateOwnerDenyList(pkgSetting);
if (listItems != null && !listItems.isEmpty()) {
- mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(), listItems);
- for (String unownedPackage : listItems) {
- PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage);
- SystemConfig config = SystemConfig.getInstance();
- if (unownedSetting != null
- && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
- unownedSetting.setUpdateOwnerPackage(null);
+ mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(),
+ listItems);
+ SystemConfig config = SystemConfig.getInstance();
+ synchronized (mPm.mLock) {
+ for (String unownedPackage : listItems) {
+ PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage);
+ if (unownedSetting != null
+ && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
+ unownedSetting.setUpdateOwnerPackage(null);
+ }
}
}
}
-
- return pkg;
}
/**
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index be4fb5c..fc83120 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -153,7 +153,7 @@
private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
@NonNull
- private ArrayList<String> mWarnings = new ArrayList<>();
+ private final ArrayList<String> mWarnings = new ArrayList<>();
// New install
InstallRequest(InstallingSession params) {
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index ca8dc29..187cada 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -68,7 +68,7 @@
final MoveInfo mMoveInfo;
final IPackageInstallObserver2 mObserver;
int mInstallFlags;
- int mDevelopmentInstallFlags;
+ final int mDevelopmentInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8b82a1c..3f4cc4a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -233,7 +233,8 @@
final LauncherAppsServiceInternal mInternal;
@NonNull
- private final RemoteCallbackList<IDumpCallback> mDumpCallbacks = new RemoteCallbackList<>();
+ private final RemoteCallbackList<IDumpCallback> mDumpCallbacks =
+ new RemoteCallbackList<>();
public LauncherAppsImpl(Context context) {
mContext = context;
@@ -2374,8 +2375,8 @@
class PackageLoadingProgressCallback extends
PackageManagerInternal.InstalledLoadingProgressCallback {
- private String mPackageName;
- private UserHandle mUser;
+ private final String mPackageName;
+ private final UserHandle mUser;
PackageLoadingProgressCallback(String packageName, UserHandle user) {
super(mCallbackHandler);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a4d8632..98eee4d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1867,7 +1867,8 @@
}
static class ParentChildSessionMap {
- private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
+ private final TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>>
+ mSessionMap;
private final Comparator<PackageInstallerSession> mSessionCreationComparator =
Comparator.comparingLong(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1be28ca..4b466be 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -491,7 +491,7 @@
@GuardedBy("mLock")
private SigningDetails mSigningDetails;
@GuardedBy("mLock")
- private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
+ private final SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
@GuardedBy("mLock")
private int mParentSessionId;
@@ -535,7 +535,7 @@
}
@GuardedBy("mLock")
- private ArraySet<FileEntry> mFiles = new ArraySet<>();
+ private final ArraySet<FileEntry> mFiles = new ArraySet<>();
static class PerFileChecksum {
private final Checksum[] mChecksums;
@@ -556,7 +556,7 @@
}
@GuardedBy("mLock")
- private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
+ private final ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
@GuardedBy("mLock")
private boolean mSessionApplied;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e5f7962..434c00a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6972,14 +6972,50 @@
final Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, uid);
extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
- extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
- mHandler.post(() -> {
+ if (android.content.pm.Flags.stayStopped()) {
+ extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
+ // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
+ mHandler.post(() -> {
+ mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
+ packageName, extras,
+ flags, null, null,
+ userIds, null, broadcastAllowList, null,
+ null);
+ });
+ } else {
mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
packageName, extras,
flags, null, null,
userIds, null, broadcastAllowList, null,
null);
- });
+ }
+ mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
+ packageName, extras, userIds, null /* instantUserIds */,
+ broadcastAllowList, mHandler);
+ }
+
+ @Override
+ public void sendPackageDataClearedBroadcast(@NonNull String packageName,
+ int uid, int userId, boolean isRestore, boolean isInstantApp) {
+ int[] visibilityAllowList =
+ snapshotComputer().getVisibilityAllowList(packageName, userId);
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
+ Uri.fromParts("package", packageName, null /* fragment */));
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ if (isRestore) {
+ intent.putExtra(Intent.EXTRA_IS_RESTORE, true);
+ }
+ if (isInstantApp) {
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ }
+ mBroadcastHelper.sendPackageBroadcastWithIntent(intent, userId, isInstantApp,
+ 0 /* flags */, visibilityAllowList, null /* finishedReceiver */,
+ null /* filterExtrasForReceiver */, null /* bOptions */);
+ mPackageMonitorCallbackHelper.notifyPackageMonitorWithIntent(intent, userId,
+ visibilityAllowList, mHandler);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 9428ef6..655b9c9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -60,7 +60,7 @@
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
public InstantAppRegistry instantAppRegistry;
- public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
+ public final ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
public InstantAppResolverConnection instantAppResolverConnection;
public ComponentName instantAppResolverSettingsComponent;
public boolean isPreNmr1Upgrade;
@@ -118,7 +118,7 @@
public SuspendPackageHelper suspendPackageHelper;
public DistractingPackageHelper distractingPackageHelper;
public StorageEventHelper storageEventHelper;
- public Set<String> initialNonStoppedSystemPackages = new ArraySet<>();
+ public final Set<String> initialNonStoppedSystemPackages = new ArraySet<>();
public boolean shouldStopSystemPackagesByDefault;
public FreeStorageHelper freeStorageHelper;
public PackageMonitorCallbackHelper packageMonitorCallbackHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d4abad8..6f45d2b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2364,7 +2364,7 @@
private boolean mSuccess = false;
private int mErrCode = -1;
private ParcelFileDescriptor mProfileReadFd = null;
- private CountDownLatch mDoneSignal = new CountDownLatch(1);
+ private final CountDownLatch mDoneSignal = new CountDownLatch(1);
@Override
public void onSuccess(ParcelFileDescriptor profileReadFd) {
@@ -5186,7 +5186,7 @@
private static class LocalIntentReceiver {
private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index a09e713..c829e1c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -148,7 +148,8 @@
private final byte[] mData;
private final String mSalt;
- private static AtomicLong sGlobalSalt = new AtomicLong((new SecureRandom()).nextLong());
+ private static final AtomicLong sGlobalSalt =
+ new AtomicLong((new SecureRandom()).nextLong());
private static Long nextGlobalSalt() {
return sGlobalSalt.incrementAndGet();
}
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 1f12c88..fa9409f 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -159,16 +159,25 @@
}
if (ArrayUtils.isEmpty(instantUserIds)) {
- doNotifyCallbacks(
+ doNotifyCallbacksByAction(
action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
} else {
- doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList, handler);
+ doNotifyCallbacksByAction(action, pkg, extras, instantUserIds, broadcastAllowList,
+ handler);
}
} catch (RemoteException e) {
// do nothing
}
}
+ void notifyPackageMonitorWithIntent(Intent intent,
+ int userId, int[] broadcastAllowList, Handler handler) {
+ if (!isAllowedCallbackAction(intent.getAction())) {
+ return;
+ }
+ doNotifyCallbacksByIntent(intent, userId, broadcastAllowList, handler);
+ }
+
private static boolean isAllowedCallbackAction(String action) {
return TextUtils.equals(action, Intent.ACTION_PACKAGE_ADDED)
|| TextUtils.equals(action, Intent.ACTION_PACKAGE_REMOVED)
@@ -177,10 +186,21 @@
|| TextUtils.equals(action, Intent.ACTION_PACKAGES_SUSPENDED)
|| TextUtils.equals(action, Intent.ACTION_PACKAGES_UNSUSPENDED)
|| TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE)
- || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
+ || TextUtils.equals(action, Intent.ACTION_PACKAGE_DATA_CLEARED)
+ || TextUtils.equals(action, Intent.ACTION_PACKAGE_RESTARTED);
}
- private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds,
+ private void doNotifyCallbacksByIntent(Intent intent, int userId,
+ int[] broadcastAllowList, Handler handler) {
+ RemoteCallbackList<IRemoteCallback> callbacks;
+ synchronized (mLock) {
+ callbacks = mCallbacks;
+ }
+ doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler);
+ }
+
+ private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
SparseArray<int[]> broadcastAllowList, Handler handler) {
RemoteCallbackList<IRemoteCallback> callbacks;
synchronized (mLock) {
@@ -200,29 +220,33 @@
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
final int[] allowUids =
- broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{};
-
- handler.post(() -> callbacks.broadcast((callback, user) -> {
- RegisterUser registerUser = (RegisterUser) user;
- if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
- != userId)) {
- return;
- }
- int registerUid = registerUser.getUid();
- if (broadcastAllowList != null && registerUid != Process.SYSTEM_UID
- && !ArrayUtils.contains(allowUids, registerUid)) {
- if (DEBUG) {
- Slog.w("PackageMonitorCallbackHelper",
- "Skip invoke PackageMonitorCallback for " + action + ", uid "
- + registerUid);
- }
- return;
- }
- invokeCallback(callback, intent);
- }));
+ broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
+ doNotifyCallbacks(callbacks, intent, userId, allowUids, handler);
}
}
+ private void doNotifyCallbacks(RemoteCallbackList<IRemoteCallback> callbacks,
+ Intent intent, int userId, int[] allowUids, Handler handler) {
+ handler.post(() -> callbacks.broadcast((callback, user) -> {
+ RegisterUser registerUser = (RegisterUser) user;
+ if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
+ != userId)) {
+ return;
+ }
+ int registerUid = registerUser.getUid();
+ if (allowUids != null && registerUid != Process.SYSTEM_UID
+ && !ArrayUtils.contains(allowUids, registerUid)) {
+ if (DEBUG) {
+ Slog.w("PackageMonitorCallbackHelper",
+ "Skip invoke PackageMonitorCallback for " + intent.getAction()
+ + ", uid " + registerUid);
+ }
+ return;
+ }
+ invokeCallback(callback, intent);
+ }));
+ }
+
private void invokeCallback(IRemoteCallback callback, Intent intent) {
try {
Bundle bundle = new Bundle();
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 5507e7c..18caafd 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -48,7 +48,7 @@
public final int mMatch;
public final ComponentName mComponent;
// Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
- public boolean mAlways;
+ public final boolean mAlways;
final String[] mSetPackages;
final String[] mSetClasses;
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index cf5aa7b..e667bfe 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -68,12 +68,12 @@
// All policy stanzas read from mac_permissions.xml. This is also the lock
// to synchronize access during policy load and access attempts.
- private static List<Policy> sPolicies = new ArrayList<>();
+ private static final List<Policy> sPolicies = new ArrayList<>();
/** Whether or not the policy files have been read */
private static boolean sPolicyRead;
/** Required MAC permissions files */
- private static List<File> sMacPermissions = new ArrayList<>();
+ private static final List<File> sMacPermissions = new ArrayList<>();
private static final String DEFAULT_SEINFO = "default";
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 440823c..6338965 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3944,9 +3944,9 @@
mDisabledSysPackages.put(name, ps);
}
- private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
- private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
- private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
+ private static final int PRE_M_APP_INFO_FLAG_HIDDEN = 1 << 27;
+ private static final int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1 << 28;
+ private static final int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1 << 30;
private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users,
ArrayMap<String, Long> originalFirstInstallTimes)
@@ -5846,7 +5846,7 @@
@GuardedBy("mLock")
// Tracking the mutations that haven't yet been written to legacy state.
// This avoids unnecessary work when writing settings for multiple users.
- private AtomicBoolean mIsLegacyPermissionStateStale = new AtomicBoolean(false);
+ private final AtomicBoolean mIsLegacyPermissionStateStale = new AtomicBoolean(false);
@GuardedBy("mLock")
// The mapping keys are user ids.
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 8667888..12115af 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -53,7 +53,7 @@
protected ShortcutUser mShortcutUser;
@GuardedBy("mLock")
- protected ShortcutBitmapSaver mShortcutBitmapSaver;
+ protected final ShortcutBitmapSaver mShortcutBitmapSaver;
protected final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c6aba2a..3adeb4b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -296,24 +296,26 @@
private final Object mNonPersistentUsersLock = new Object();
private final Object mWtfLock = new Object();
- private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
+ private static final List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
// Temporarily reverted to anonymous inner class form due to: b/32554459
- private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED = new Predicate<ResolveInfo>() {
- public boolean test(ResolveInfo ri) {
- return !ri.activityInfo.exported;
- }
- };
+ private static final Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED =
+ new Predicate<ResolveInfo>() {
+ public boolean test(ResolveInfo ri) {
+ return !ri.activityInfo.exported;
+ }
+ };
- private static Predicate<ResolveInfo> ACTIVITY_NOT_INSTALLED = (ri) ->
+ private static final Predicate<ResolveInfo> ACTIVITY_NOT_INSTALLED = (ri) ->
!isInstalled(ri.activityInfo);
// Temporarily reverted to anonymous inner class form due to: b/32554459
- private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = new Predicate<PackageInfo>() {
- public boolean test(PackageInfo pi) {
- return !isInstalled(pi);
- }
- };
+ private static final Predicate<PackageInfo> PACKAGE_NOT_INSTALLED =
+ new Predicate<PackageInfo>() {
+ public boolean test(PackageInfo pi) {
+ return !isInstalled(pi);
+ }
+ };
private final Handler mHandler;
@@ -4656,8 +4658,8 @@
private boolean mDumpFiles = false;
private boolean mDumpDetails = true;
- private List<Pattern> mPackagePatterns = new ArrayList<>();
- private List<Integer> mUsers = new ArrayList<>();
+ private final List<Pattern> mPackagePatterns = new ArrayList<>();
+ private final List<Integer> mUsers = new ArrayList<>();
void addPackageRegex(String regex) {
mPackagePatterns.add(Pattern.compile(regex));
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
index e04a1e5..31541d0 100644
--- a/services/core/java/com/android/server/pm/SnapshotStatistics.java
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -157,12 +157,12 @@
private static class BinMap {
// The number of bins
- private int mCount;
+ private final int mCount;
// The maximum mapped value. Values at or above this are mapped to the
// top bin.
- private int mMaxBin;
+ private final int mMaxBin;
// A copy of the original key
- private int[] mUserKey;
+ private final int[] mUserKey;
/**
* Create a bin map. The input is an array of integers, which must be
@@ -232,13 +232,13 @@
* The build-time histogram. The total number of rebuilds is the sum over the
* histogram entries.
*/
- public int[] mTimes;
+ public final int[] mTimes;
/**
* The reuse histogram. The total number of snapshot uses is the sum over the
* histogram entries.
*/
- public int[] mUsed;
+ public final int[] mUsed;
/**
* The total number of rebuilds. This could be computed by summing over the use
@@ -477,12 +477,12 @@
/**
* Long statistics. These roll over approximately one day.
*/
- private Stats[] mLong;
+ private final Stats[] mLong;
/**
* Short statistics. These roll over approximately every minute;
*/
- private Stats[] mShort;
+ private final Stats[] mShort;
/**
* The time of last logging to the FrameworkStatsLog.
diff --git a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
index 43752f3..adac68b 100644
--- a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
+++ b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
@@ -48,7 +48,7 @@
private final Object mLock = new Object();
- private static boolean hasValidOwnershipDenyList(PackageSetting pkgSetting) {
+ static boolean hasValidOwnershipDenyList(PackageSetting pkgSetting) {
AndroidPackage pkg = pkgSetting.getPkg();
// we're checking for uses-permission for these priv permissions instead of grant as we're
// only considering system apps to begin with, so presumed to be granted.
diff --git a/services/core/java/com/android/server/pm/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java
index 895edce..651578d 100644
--- a/services/core/java/com/android/server/pm/UserJourneyLogger.java
+++ b/services/core/java/com/android/server/pm/UserJourneyLogger.java
@@ -586,7 +586,7 @@
public final long mSessionId;
@UserJourney
public final int mJourney;
- public long mStartTimeInMills;
+ public final long mStartTimeInMills;
@VisibleForTesting
public UserJourneySession(long sessionId, @UserJourney int journey) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6e738da..78c13f8 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -119,10 +119,10 @@
private final int mCriticalBatteryLevel;
// Possible outcomes of a dex search.
- private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
- private static int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk
- private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
- private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
+ private static final int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
+ private static final int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk
+ private static final int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
+ private static final int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
public DexManager(Context context, PackageDexOptimizer pdo, Installer installer,
Object installLock, DynamicCodeLogger dynamicCodeLogger) {
@@ -959,8 +959,8 @@
* Convenience class to store ownership search results.
*/
private class DexSearchResult {
- private String mOwningPackageName;
- private int mOutcome;
+ private final String mOwningPackageName;
+ private final int mOutcome;
public DexSearchResult(String owningPackageName, int outcome) {
this.mOwningPackageName = owningPackageName;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index d3a64bb..2ab7db4 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -49,7 +49,7 @@
public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();
@NonNull
- private File mCacheDir;
+ private final File mCacheDir;
public PackageCacher(@NonNull File cacheDir) {
this.mCacheDir = cacheDir;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 9e20805..c26cf1c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -1132,7 +1132,7 @@
*/
public static class CachedApplicationInfoGenerator {
// Map from a package name to the corresponding app info.
- private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
+ private final ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
/**
* {@link PackageInfoUtils#generateApplicationInfo} with a cache.
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index d82a500..3b10c7f 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -96,19 +96,19 @@
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
- private ThreadLocal<ApplicationInfo> mSharedAppInfo =
+ private final ThreadLocal<ApplicationInfo> mSharedAppInfo =
ThreadLocal.withInitial(() -> {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = -1; // Not a valid UID since the app will not be installed yet
return appInfo;
});
- private ThreadLocal<ParseTypeImpl> mSharedResult;
+ private final ThreadLocal<ParseTypeImpl> mSharedResult;
@Nullable
protected PackageCacher mCacher;
- private ParsingPackageUtils parsingUtils;
+ private final ParsingPackageUtils parsingUtils;
public PackageParser2(String[] separateProcesses, DisplayMetrics displayMetrics,
@Nullable File cacheDir, @NonNull Callback callback) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 056aae4..75dd67d 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -113,21 +113,21 @@
private static final SparseArray<int[]> EMPTY_INT_ARRAY_SPARSE_ARRAY = new SparseArray<>();
private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
(first, second) -> Integer.compare(second.getOrder(), first.getOrder());
- public static Parcelling.BuiltIn.ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(
+ public static final Parcelling.BuiltIn.ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(
Parcelling.BuiltIn.ForBoolean.class);
- public static ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
+ public static final ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
ForInternedString.class);
- public static Parcelling.BuiltIn.ForInternedStringArray sForInternedStringArray = Parcelling.Cache.getOrCreate(
- Parcelling.BuiltIn.ForInternedStringArray.class);
- public static Parcelling.BuiltIn.ForInternedStringList sForInternedStringList = Parcelling.Cache.getOrCreate(
- Parcelling.BuiltIn.ForInternedStringList.class);
- public static Parcelling.BuiltIn.ForInternedStringValueMap sForInternedStringValueMap =
+ public static final Parcelling.BuiltIn.ForInternedStringArray sForInternedStringArray =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringArray.class);
+ public static final Parcelling.BuiltIn.ForInternedStringList sForInternedStringList =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringList.class);
+ public static final Parcelling.BuiltIn.ForInternedStringValueMap sForInternedStringValueMap =
Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringValueMap.class);
- public static Parcelling.BuiltIn.ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(
- Parcelling.BuiltIn.ForStringSet.class);
- public static Parcelling.BuiltIn.ForInternedStringSet sForInternedStringSet =
+ public static final Parcelling.BuiltIn.ForStringSet sForStringSet =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
+ public static final Parcelling.BuiltIn.ForInternedStringSet sForInternedStringSet =
Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringSet.class);
- protected static ParsingUtils.StringPairListParceler sForIntentInfoPairs =
+ protected static final ParsingUtils.StringPairListParceler sForIntentInfoPairs =
new ParsingUtils.StringPairListParceler();
protected int versionCode;
protected int versionCodeMajor;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index d6a7dc6..6f6bb45 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1748,14 +1748,14 @@
*/
private class DelayingPackageManagerCache extends PackageManagerWrapper {
/** uid -> permission -> isGranted, flags */
- private SparseArray<ArrayMap<String, PermissionState>> mDelayedPermissionState =
+ private final SparseArray<ArrayMap<String, PermissionState>> mDelayedPermissionState =
new SparseArray<>();
/** userId -> context */
- private SparseArray<Context> mUserContexts = new SparseArray<>();
+ private final SparseArray<Context> mUserContexts = new SparseArray<>();
/** Permission name -> info */
- private ArrayMap<String, PermissionInfo> mPermissionInfos = new ArrayMap<>();
+ private final ArrayMap<String, PermissionInfo> mPermissionInfos = new ArrayMap<>();
/** Package name -> info */
- private ArrayMap<String, PackageInfo> mPackageInfos = new ArrayMap<>();
+ private final ArrayMap<String, PackageInfo> mPackageInfos = new ArrayMap<>();
/**
* Apply the cached state
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
index 70986c3..4c831d3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
@@ -41,7 +41,8 @@
public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission,
Parcelable {
- private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+ private static final ForStringSet sForStringSet =
+ Parcelling.Cache.getOrCreate(ForStringSet.class);
@Nullable
private String backgroundPermission;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 8240c47..061698a 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -328,11 +328,11 @@
return input.success(pkg);
}
- private String[] mSeparateProcesses;
- private DisplayMetrics mDisplayMetrics;
+ private final String[] mSeparateProcesses;
+ private final DisplayMetrics mDisplayMetrics;
@NonNull
- private List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
- private Callback mCallback;
+ private final List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
+ private final Callback mCallback;
public ParsingPackageUtils(String[] separateProcesses, DisplayMetrics displayMetrics,
@NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index fac681a..0ceda42 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -951,7 +951,7 @@
extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
@NonNull
- private UserNeedsBadgingCache mUserNeedsBadging;
+ private final UserNeedsBadgingCache mUserNeedsBadging;
// Default constructor
ActivityIntentResolver(@NonNull UserManagerService userManager,
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
index 9115775..80cde73 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -68,7 +68,7 @@
protected ArrayMap<String, ParsedProvider> mProvidersByAuthority;
@NonNull
- protected UserManagerService mUserManager;
+ protected final UserManagerService mUserManager;
protected ComponentResolverBase(@NonNull UserManagerService userManager) {
mUserManager = userManager;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 11f62e9..3d4d4ec 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -2005,9 +2005,9 @@
private static class GetAttachedResult {
@Nullable
- private DomainVerificationPkgState mPkgState;
+ private final DomainVerificationPkgState mPkgState;
- private int mErrorCode;
+ private final int mErrorCode;
GetAttachedResult(@Nullable DomainVerificationPkgState pkgState, int errorCode) {
mPkgState = pkgState;
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index 6c56360..d71dbbb 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -40,7 +40,7 @@
private final String mPackageName;
@NonNull
- private UUID mId;
+ private final UUID mId;
/**
* Whether or not the package declares any autoVerify domains. This is separate from an empty
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index c2821ae..b83421f 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -50,12 +50,11 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintConsumer;
import com.android.internal.util.function.QuintFunction;
-import com.android.internal.util.function.TriConsumer;
-import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
@@ -230,12 +229,10 @@
}
@Override
- public int checkOperation(int code, AttributionSource attributionSource, boolean raw,
- TriFunction<Integer, AttributionSource, Boolean, Integer> superImpl) {
- final int uid = attributionSource.getUid();
- final AttributionSource resolvedAttributionSource =
- attributionSource.withUid(resolveUid(code, uid));
- return superImpl.apply(code, resolvedAttributionSource, raw);
+ public int checkOperation(int code, int uid, String packageName,
+ @Nullable String attributionTag, boolean raw,
+ QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
+ return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag, raw);
}
@Override
@@ -245,25 +242,21 @@
}
@Override
- public SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
- boolean shouldCollectAsyncNotedOp, @Nullable
- String message, boolean shouldCollectMessage,
- @NonNull QuintFunction<Integer, AttributionSource, Boolean, String, Boolean,
- SyncNotedAppOp> superImpl) {
- final int uid = attributionSource.getUid();
- final AttributionSource resolvedAttributionSource =
- attributionSource.withUid(resolveUid(code, uid));
- return superImpl.apply(resolveDatasourceOp(code, uid, attributionSource.getPackageName(),
- attributionSource.getAttributionTag()), resolvedAttributionSource,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+ public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
+ @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
+ String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
+ String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
+ return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
+ resolveUid(code, uid), packageName, attributionTag, shouldCollectAsyncNotedOp,
+ message, shouldCollectMessage);
}
@Override
public SyncNotedAppOp noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
boolean shouldCollectAsyncNotedOp, @Nullable String message,
boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
- AttributionSource, Boolean, String, Boolean, Boolean,
- SyncNotedAppOp> superImpl) {
+ AttributionSource, Boolean, String, Boolean, Boolean,
+ SyncNotedAppOp> superImpl) {
return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
attributionSource.getPackageName(), attributionSource.getAttributionTag()),
attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
@@ -271,21 +264,17 @@
}
@Override
- public SyncNotedAppOp startOperation(IBinder token, int code,
- AttributionSource attributionSource,
+ public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
+ @Nullable String packageName, @Nullable String attributionTag,
boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
- int attributionChainId,
- @NonNull NonaFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String,
- Boolean, Integer, Integer,
- SyncNotedAppOp> superImpl) {
- final int uid = attributionSource.getUid();
- final AttributionSource resolvedAttributionSource =
- attributionSource.withUid(resolveUid(code, uid));
- return superImpl.apply(token, resolveDatasourceOp(code, uid,
- attributionSource.getPackageName(), attributionSource.getAttributionTag()),
- resolvedAttributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
- shouldCollectMessage, attributionFlags, attributionChainId);
+ int attributionChainId, @NonNull UndecFunction<IBinder, Integer, Integer, String,
+ String, Boolean, Boolean, String, Boolean, Integer, Integer,
+ SyncNotedAppOp> superImpl) {
+ return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag),
+ resolveUid(code, uid), packageName, attributionTag, startIfModeDefault,
+ shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
+ attributionChainId);
}
@Override
@@ -304,14 +293,11 @@
}
@Override
- public void finishOperation(IBinder clientId, int code, AttributionSource attributionSource,
- @NonNull TriConsumer<IBinder, Integer, AttributionSource> superImpl) {
- final int uid = attributionSource.getUid();
- final AttributionSource resolvedAttributionSource =
- attributionSource.withUid(resolveUid(code, uid));
- superImpl.accept(clientId, resolveDatasourceOp(code, uid,
- attributionSource.getPackageName(), attributionSource.getAttributionTag()),
- resolvedAttributionSource);
+ public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag,
+ @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
+ superImpl.accept(clientId, resolveDatasourceOp(code, uid, packageName, attributionTag),
+ resolveUid(code, uid), packageName, attributionTag);
}
@Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 45ca690..cf1036c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -549,6 +549,7 @@
int mLidNavigationAccessibility;
int mShortPressOnPowerBehavior;
private boolean mShouldEarlyShortPressOnPower;
+ private boolean mShouldEarlyShortPressOnStemPrimary;
int mLongPressOnPowerBehavior;
long mLongPressOnPowerAssistantTimeoutMs;
int mVeryLongPressOnPowerBehavior;
@@ -2748,6 +2749,9 @@
@Override
void onPress(long downTime) {
+ if (mShouldEarlyShortPressOnStemPrimary) {
+ return;
+ }
stemPrimaryPress(1 /*count*/);
}
@@ -2760,6 +2764,13 @@
void onMultiPress(long downTime, int count) {
stemPrimaryPress(count);
}
+
+ @Override
+ void onKeyUp(long eventTime, int count) {
+ if (mShouldEarlyShortPressOnStemPrimary && count == 1) {
+ stemPrimaryPress(1 /*pressCount*/);
+ }
+ }
}
private void initSingleKeyGestureRules(Looper looper) {
@@ -2929,6 +2940,8 @@
mShouldEarlyShortPressOnPower =
mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_shortPressEarlyOnPower);
+ mShouldEarlyShortPressOnStemPrimary = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_shortPressEarlyOnStemPrimary);
mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 3dbd2f8..6a0fe90 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -395,7 +395,8 @@
@VisibleForTesting
int getDesiredScreenPolicyLocked(boolean quiescent, boolean dozeAfterScreenOff,
- boolean bootCompleted, boolean screenBrightnessBoostInProgress) {
+ boolean bootCompleted, boolean screenBrightnessBoostInProgress,
+ boolean brightWhenDozing) {
final int wakefulness = getWakefulnessLocked();
final int wakeLockSummary = getWakeLockSummaryLocked();
if (wakefulness == WAKEFULNESS_ASLEEP || quiescent) {
@@ -407,8 +408,12 @@
if (dozeAfterScreenOff) {
return DisplayPowerRequest.POLICY_OFF;
}
+ if (brightWhenDozing) {
+ return DisplayPowerRequest.POLICY_BRIGHT;
+ }
// Fall through and preserve the current screen policy if not configured to
- // doze after screen off. This causes the screen off transition to be skipped.
+ // bright when dozing or doze after screen off. This causes the screen off transition
+ // to be skipped.
}
if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
@@ -429,9 +434,10 @@
boolean boostScreenBrightness, int dozeScreenState, float dozeScreenBrightness,
boolean overrideDrawWakeLock, PowerSaveState powerSaverState, boolean quiescent,
boolean dozeAfterScreenOff, boolean bootCompleted,
- boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity) {
+ boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity,
+ boolean brightWhenDozing) {
mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(quiescent, dozeAfterScreenOff,
- bootCompleted, screenBrightnessBoostInProgress);
+ bootCompleted, screenBrightnessBoostInProgress, brightWhenDozing);
mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
mDisplayPowerRequest.useProximitySensor = useProximitySensor;
mDisplayPowerRequest.boostScreenBrightness = boostScreenBrightness;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8ce0c72..ec5172f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -546,6 +546,10 @@
// True if doze should not be started until after the screen off transition.
private boolean mDozeAfterScreenOff;
+ // True if bright policy should be applied when we have entered dozing wakefulness but haven't
+ // started doze component.
+ private boolean mBrightWhenDozingConfig;
+
// The minimum screen off timeout, in milliseconds.
private long mMinimumScreenOffTimeoutConfig;
@@ -1492,6 +1496,8 @@
com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
mDozeAfterScreenOff = resources.getBoolean(
com.android.internal.R.bool.config_dozeAfterScreenOffByDefault);
+ mBrightWhenDozingConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_brightWhenDozing);
mMinimumScreenOffTimeoutConfig = resources.getInteger(
com.android.internal.R.integer.config_minimumScreenOffTimeout);
mMaximumScreenDimDurationConfig = resources.getInteger(
@@ -3560,7 +3566,8 @@
.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS)
: new PowerSaveState.Builder().build(),
sQuiescent, mDozeAfterScreenOff, mBootCompleted,
- mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity);
+ mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity,
+ mBrightWhenDozingConfig);
int wakefulness = powerGroup.getWakefulnessLocked();
if (DEBUG_SPEW) {
Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
@@ -3635,7 +3642,7 @@
int getDesiredScreenPolicyLocked(int groupId) {
return mPowerGroups.get(groupId).getDesiredScreenPolicyLocked(sQuiescent,
mDozeAfterScreenOff, mBootCompleted,
- mScreenBrightnessBoostInProgress);
+ mScreenBrightnessBoostInProgress, mBrightWhenDozingConfig);
}
@VisibleForTesting
@@ -4655,6 +4662,7 @@
pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
pw.println(" mDozeAfterScreenOff=" + mDozeAfterScreenOff);
+ pw.println(" mBrightWhenDozingConfig=" + mBrightWhenDozingConfig);
pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index a388932..b8e581f 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -592,6 +592,10 @@
public void dumpCpuPowerBracketsLocked(PrintWriter pw) {
ensureInitialized();
+ if (mLayout == null) {
+ return;
+ }
+
pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):");
for (int bracket = 0; bracket < mLayout.getCpuPowerBracketCount(); bracket++) {
pw.print(" ");
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
index f1bfea7..5c37680 100644
--- a/services/core/java/com/android/server/timedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -1,14 +1,6 @@
{
"presubmit": [
{
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timedetector."
- }
- ]
- },
- {
"name": "CtsTimeTestCases",
"options": [
{
@@ -16,5 +8,11 @@
}
]
}
+ ],
+ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+ "postsubmit": [
+ {
+ "name": "FrameworksTimeServicesTests"
+ }
]
}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index d88f426..83270f6 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.time.ExternalTimeSuggestion;
import android.app.time.ITimeDetectorListener;
import android.app.time.TimeCapabilitiesAndConfig;
@@ -30,7 +29,6 @@
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.content.Context;
-import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelableException;
@@ -166,8 +164,7 @@
*/
boolean updateConfiguration(@UserIdInt int userId, @NonNull TimeConfiguration configuration) {
// Resolve constants like USER_CURRENT to the true user ID as needed.
- int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, false, "updateConfiguration", null);
+ int resolvedUserId = mCallerIdentityInjector.resolveUserId(userId, "updateConfiguration");
enforceManageTimeDetectorPermission();
@@ -280,11 +277,11 @@
public TimeState getTimeState() {
enforceManageTimeDetectorPermission();
- final long token = Binder.clearCallingIdentity();
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
return mTimeDetectorStrategy.getTimeState();
} finally {
- Binder.restoreCallingIdentity(token);
+ mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@@ -295,11 +292,11 @@
void setTimeState(@NonNull TimeState timeState) {
enforceManageTimeDetectorPermission();
- final long token = Binder.clearCallingIdentity();
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
mTimeDetectorStrategy.setTimeState(timeState);
} finally {
- Binder.restoreCallingIdentity(token);
+ mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@@ -308,11 +305,11 @@
enforceManageTimeDetectorPermission();
Objects.requireNonNull(time);
- final long token = Binder.clearCallingIdentity();
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
return mTimeDetectorStrategy.confirmTime(time);
} finally {
- Binder.restoreCallingIdentity(token);
+ mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@@ -324,13 +321,13 @@
// This calls suggestManualTime() as the logic is identical, it only differs in the
// permission required, which is handled on the line above.
int userId = mCallerIdentityInjector.getCallingUserId();
- final long token = Binder.clearCallingIdentity();
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
final boolean bypassUserPolicyChecks = false;
return mTimeDetectorStrategy.suggestManualTime(
userId, suggestion, bypassUserPolicyChecks);
} finally {
- Binder.restoreCallingIdentity(token);
+ mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@@ -377,11 +374,11 @@
void clearLatestNetworkTime() {
enforceSuggestNetworkTimePermission();
- final long token = Binder.clearCallingIdentity();
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
mTimeDetectorStrategy.clearLatestNetworkSuggestion();
} finally {
- Binder.restoreCallingIdentity(token);
+ mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
@@ -473,7 +470,7 @@
void clearNetworkTimeForSystemClockForTests() {
enforceSuggestNetworkTimePermission();
- final long token = Binder.clearCallingIdentity();
+ final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
// TODO(b/222295093): Remove this condition once we can be sure that all uses of
// NtpTrustedTime result in a suggestion being made to the time detector.
@@ -485,7 +482,7 @@
mNtpTrustedTime.clearCachedTimeResult();
}
} finally {
- Binder.restoreCallingIdentity(token);
+ mCallerIdentityInjector.restoreCallingIdentity(token);
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
index 1500cfa..1f1d83f 100644
--- a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
+++ b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
@@ -17,6 +17,7 @@
package com.android.server.timezonedetector;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.os.Binder;
import android.os.UserHandle;
@@ -29,6 +30,12 @@
/** A singleton for the real implementation of {@link CallerIdentityInjector}. */
CallerIdentityInjector REAL = new Real();
+ /**
+ * A {@link ActivityManager#handleIncomingUser} call. This can be used to map the abstract
+ * user ID value USER_CURRENT to the actual user ID.
+ */
+ @UserIdInt int resolveUserId(@UserIdInt int userId, String debugInfo);
+
/** A {@link UserHandle#getCallingUserId()} call. */
@UserIdInt int getCallingUserId();
@@ -45,6 +52,12 @@
}
@Override
+ public int resolveUserId(@UserIdInt int userId, String debugName) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, false, debugName, null);
+ }
+
+ @Override
public int getCallingUserId() {
return UserHandle.getCallingUserId();
}
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 455accd..63dd7b4 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -1,14 +1,6 @@
{
"presubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timezonedetector."
- }
- ]
- },
- {
+ {
"name": "CtsTimeTestCases",
"options": [
{
@@ -21,6 +13,9 @@
"postsubmit": [
{
"name": "CtsLocationTimeZoneManagerHostTest"
+ },
+ {
+ "name": "FrameworksTimeServicesTests"
}
]
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index dac4bf8..d914544 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.app.time.ITimeZoneDetectorListener;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
@@ -28,7 +27,6 @@
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.content.Context;
-import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -168,8 +166,8 @@
enforceManageTimeZoneDetectorPermission();
// Resolve constants like USER_CURRENT to the true user ID as needed.
- int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, false, "getCapabilitiesAndConfig", null);
+ int resolvedUserId =
+ mCallerIdentityInjector.resolveUserId(userId, "getCapabilitiesAndConfig");
final long token = mCallerIdentityInjector.clearCallingIdentity();
try {
@@ -190,8 +188,7 @@
boolean updateConfiguration(
@UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
// Resolve constants like USER_CURRENT to the true user ID as needed.
- int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, false, "updateConfiguration", null);
+ int resolvedUserId = mCallerIdentityInjector.resolveUserId(userId, "updateConfiguration");
enforceManageTimeZoneDetectorPermission();
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 52ab9b8..86be6ba 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -237,22 +237,8 @@
}
if (containsFile(code, userId)) {
synchronized (mSnapshotPersistQueue.getLock()) {
- mSnapshotPersistQueue.sendToQueueLocked(
- new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) {
- @Override
- void write() {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
- "load_activity_snapshot");
- final TaskSnapshot snapshot = mSnapshotLoader.loadTask(code,
- userId, false /* loadLowResolutionBitmap */);
- synchronized (mService.getWindowManagerLock()) {
- if (snapshot != null && !ar.finishing) {
- mCache.putSnapshot(ar, snapshot);
- }
- }
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
- });
+ mSnapshotPersistQueue.insertQueueAtFirstLocked(
+ new LoadActivitySnapshotItem(ar, code, userId, mPersistInfoProvider));
}
}
}
@@ -273,6 +259,42 @@
resetTmpFields();
}
+ class LoadActivitySnapshotItem extends SnapshotPersistQueue.WriteQueueItem {
+ private final int mCode;
+ private final int mUserId;
+ private final ActivityRecord mActivityRecord;
+
+ LoadActivitySnapshotItem(@NonNull ActivityRecord ar, int code, int userId,
+ @NonNull PersistInfoProvider persistInfoProvider) {
+ super(persistInfoProvider);
+ mActivityRecord = ar;
+ mCode = code;
+ mUserId = userId;
+ }
+
+ @Override
+ void write() {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+ "load_activity_snapshot");
+ final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode,
+ mUserId, false /* loadLowResolutionBitmap */);
+ synchronized (mService.getWindowManagerLock()) {
+ if (snapshot != null && !mActivityRecord.finishing) {
+ mCache.putSnapshot(mActivityRecord, snapshot);
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) return false;
+ final LoadActivitySnapshotItem other = (LoadActivitySnapshotItem) o;
+ return mCode == other.mCode && mUserId == other.mUserId
+ && mPersistInfoProvider == other.mPersistInfoProvider;
+ }
+ }
+
void recordSnapshot(ActivityRecord activity) {
if (shouldDisableSnapshots()) {
return;
@@ -571,7 +593,8 @@
for (int i = savedFileCount - 1; i > removeTillIndex; --i) {
final UserSavedFile usf = mSavedFilesInOrder.remove(i);
if (usf != null) {
- mUserSavedFiles.remove(usf.mFileId);
+ final SparseArray<UserSavedFile> records = getUserFiles(usf.mUserId);
+ records.remove(usf.mFileId);
usfs.add(usf);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 4a5311b..2c49203 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -269,12 +269,27 @@
}
}
+ /**
+ * Start intent as a package.
+ *
+ * @param uid Make a call as if this UID did.
+ * @param callingPackage Make a call as if this package did.
+ * @param callingFeatureId Make a call as if this feature in the package did.
+ * @param intent Intent to start.
+ * @param userId Start the intents on this user.
+ * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+ * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+ * null if not originated by PendingIntent
+ * @param forcedBalByPiSender If set to allow, the
+ * PendingIntent's sender will try to force allow background activity starts.
+ * This is only possible if the sender of the PendingIntent is a system process.
+ */
final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges forcedBalByPiSender) {
userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
reason);
@@ -295,7 +310,7 @@
.setUserId(userId)
.setInTask(inTask)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setBackgroundStartPrivileges(backgroundStartPrivileges)
+ .setBackgroundStartPrivileges(forcedBalByPiSender)
.execute();
}
@@ -310,15 +325,18 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
+ * @param forcedBalByPiSender If set to allow, the
+ * PendingIntent's sender will try to force allow background activity starts.
+ * This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivitiesInPackage(int uid, String callingPackage,
@Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges forcedBalByPiSender) {
return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
- validateIncomingUser, originatingPendingIntent, backgroundStartPrivileges);
+ validateIncomingUser, originatingPendingIntent, forcedBalByPiSender);
}
/**
@@ -333,12 +351,15 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
+ * @param forcedBalByPiSender If set to allow, the
+ * PendingIntent's sender will try to force allow background activity starts.
+ * This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges forcedBalByPiSender) {
final String reason = "startActivityInPackage";
@@ -348,14 +369,14 @@
// TODO: Switch to user app stacks here.
return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
- originatingPendingIntent, backgroundStartPrivileges);
+ originatingPendingIntent, forcedBalByPiSender);
}
int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
int userId, String reason, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges forcedBalByPiSender) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -463,7 +484,7 @@
// top one as otherwise an activity below might consume it.
.setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setBackgroundStartPrivileges(backgroundStartPrivileges);
+ .setBackgroundStartPrivileges(forcedBalByPiSender);
}
// Log if the activities to be started have different uids.
if (startingUidPkgs.size() > 1) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 34bf8ed..009b8e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -388,7 +388,7 @@
WaitResult waitResult;
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
- BackgroundStartPrivileges backgroundStartPrivileges;
+ BackgroundStartPrivileges forcedBalByPiSender;
final StringBuilder logMessage = new StringBuilder();
@@ -451,7 +451,7 @@
allowPendingRemoteAnimationRegistryLookup = true;
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
- backgroundStartPrivileges = BackgroundStartPrivileges.NONE;
+ forcedBalByPiSender = BackgroundStartPrivileges.NONE;
errorCallbackToken = null;
}
@@ -494,7 +494,7 @@
= request.allowPendingRemoteAnimationRegistryLookup;
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
- backgroundStartPrivileges = request.backgroundStartPrivileges;
+ forcedBalByPiSender = request.forcedBalByPiSender;
errorCallbackToken = request.errorCallbackToken;
}
@@ -1106,7 +1106,7 @@
realCallingPid,
callerApp,
request.originatingPendingIntent,
- request.backgroundStartPrivileges,
+ request.forcedBalByPiSender,
intent,
checkedOptions);
balCode = balVerdict.getCode();
@@ -3167,9 +3167,8 @@
return this;
}
- ActivityStarter setBackgroundStartPrivileges(
- BackgroundStartPrivileges backgroundStartPrivileges) {
- mRequest.backgroundStartPrivileges = backgroundStartPrivileges;
+ ActivityStarter setBackgroundStartPrivileges(BackgroundStartPrivileges forcedBalByPiSender) {
+ mRequest.forcedBalByPiSender = forcedBalByPiSender;
return this;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index a2547fd..5604b1a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -215,21 +215,39 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param allowBackgroundActivityStart Whether the background activity start should be allowed
- * from originatingPendingIntent
+ * @param forcedBalByPiSender If set to allow, the
+ * PendingIntent's sender will try to force allow background activity starts.
+ * This is only possible if the sender of the PendingIntent is a system process.
*/
public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges);
+ BackgroundStartPrivileges forcedBalByPiSender);
+ /**
+ * Start intent as a package.
+ *
+ * @param uid Make a call as if this UID did.
+ * @param realCallingPid PID of the real caller.
+ * @param realCallingUid UID of the real caller.
+ * @param callingPackage Make a call as if this package did.
+ * @param callingFeatureId Make a call as if this feature in the package did.
+ * @param intent Intent to start.
+ * @param userId Start the intents on this user.
+ * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+ * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+ * null if not originated by PendingIntent
+ * @param forcedBalByPiSender If set to allow, the
+ * PendingIntent's sender will try to force allow background activity starts.
+ * This is only possible if the sender of the PendingIntent is a system process.
+ */
public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
- String callingPackage, @Nullable String callingFeaturId, Intent intent,
+ String callingPackage, @Nullable String callingFeatureId, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges);
+ BackgroundStartPrivileges forcedBalByPiSender);
/**
* Callback to be called on certain activity start scenarios.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f0698be..3c56a4e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -125,7 +125,6 @@
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext;
import android.Manifest;
import android.annotation.IntDef;
@@ -166,7 +165,6 @@
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.compat.CompatChanges;
-import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -1260,13 +1258,6 @@
true /*validateIncomingUser*/);
}
- static boolean isSdkSandboxActivity(Context context, Intent intent) {
- return intent != null
- && (sandboxActivitySdkBasedContext()
- ? SdkSandboxActivityAuthority.isSdkSandboxActivity(context, intent)
- : intent.isSandboxActivity(context));
- }
-
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
@@ -1277,7 +1268,7 @@
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
- if (isSdkSandboxActivity(mContext, intent)) {
+ if (intent != null && intent.isSandboxActivity(mContext)) {
SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
SdkSandboxManagerLocal.class);
sdkSandboxManagerLocal.enforceAllowedToHostSandboxedActivity(
@@ -3868,10 +3859,14 @@
Slog.w(TAG, "takeTaskSnapshot: taskId=" + taskId + " not found or not visible");
return null;
}
+ // Note that if updateCache is true, ActivityRecord#shouldUseAppThemeSnapshot will
+ // be used to decide whether the task is allowed to be captured because that may
+ // be retrieved by recents. While if updateCache is false, the real snapshot will
+ // always be taken and the snapshot won't be put into SnapshotPersister.
if (updateCache) {
return mWindowManager.mTaskSnapshotController.recordSnapshot(task);
} else {
- return mWindowManager.mTaskSnapshotController.captureSnapshot(task);
+ return mWindowManager.mTaskSnapshotController.snapshot(task);
}
}
} finally {
@@ -5920,12 +5915,12 @@
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges forcedBalByPiSender) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
- backgroundStartPrivileges);
+ forcedBalByPiSender);
}
@Override
@@ -5934,13 +5929,13 @@
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges forcedBalByPiSender) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
reason, validateIncomingUser, originatingPendingIntent,
- backgroundStartPrivileges);
+ forcedBalByPiSender);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e5eb303..777b5cd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1089,7 +1089,7 @@
// Remove the process record so it won't be considered as alive.
mService.mProcessNames.remove(wpc.mName, wpc.mUid);
mService.mProcessMap.remove(wpc.getPid());
- } else if (ActivityTaskManagerService.isSdkSandboxActivity(mService.mContext, r.intent)) {
+ } else if (r.intent.isSandboxActivity(mService.mContext)) {
Slog.e(TAG, "Abort sandbox activity launching as no sandbox process to host it.");
r.finishIfPossible("No sandbox process for the activity", false /* oomAdj */);
r.launchFailed = true;
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index cdaab46..1b5631f5 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -30,6 +30,7 @@
import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
import static com.android.window.flags.Flags.balShowToasts;
+import static com.android.window.flags.Flags.balShowToastsBlocked;
import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -194,6 +195,10 @@
return activity != null && packageName.equals(activity.getPackageName());
}
+ /**
+ * @see #checkBackgroundActivityStart(int, int, String, int, int, WindowProcessController,
+ * PendingIntentRecord, BackgroundStartPrivileges, Intent, ActivityOptions)
+ */
boolean shouldAbortBackgroundActivityStart(
int callingUid,
int callingPid,
@@ -202,13 +207,13 @@
int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges,
+ BackgroundStartPrivileges forcedBalByPiSender,
Intent intent,
ActivityOptions checkedOptions) {
return checkBackgroundActivityStart(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid,
callerApp, originatingPendingIntent,
- backgroundStartPrivileges, intent, checkedOptions).blocks();
+ forcedBalByPiSender, intent, checkedOptions).blocks();
}
private class BalState {
@@ -229,7 +234,7 @@
private final @ActivityManager.ProcessState int mRealCallingUidProcState;
private final boolean mIsRealCallingUidPersistentSystemProcess;
private final PendingIntentRecord mOriginatingPendingIntent;
- private final BackgroundStartPrivileges mBackgroundStartPrivileges;
+ private final BackgroundStartPrivileges mForcedBalByPiSender;
private final Intent mIntent;
private final WindowProcessController mCallerApp;
private final WindowProcessController mRealCallerApp;
@@ -238,7 +243,7 @@
int realCallingUid, int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges,
+ BackgroundStartPrivileges forcedBalByPiSender,
Intent intent,
ActivityOptions checkedOptions) {
this.mCallingPackage = callingPackage;
@@ -247,7 +252,7 @@
mRealCallingUid = realCallingUid;
mRealCallingPid = realCallingPid;
mCallerApp = callerApp;
- mBackgroundStartPrivileges = backgroundStartPrivileges;
+ mForcedBalByPiSender = forcedBalByPiSender;
mOriginatingPendingIntent = originatingPendingIntent;
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
@@ -321,6 +326,7 @@
.append(getDebugPackageName(mCallingPackage, mCallingUid));
sb.append("; callingUid: ").append(mCallingUid);
sb.append("; callingPid: ").append(mCallingPid);
+ sb.append("; isPendingIntent: ").append(isPendingIntent());
sb.append("; appSwitchState: ").append(mAppSwitchState);
sb.append("; callingUidHasAnyVisibleWindow: ").append(mCallingUidHasAnyVisibleWindow);
sb.append("; callingUidProcState: ").append(DebugUtils.valueToString(
@@ -328,7 +334,7 @@
sb.append("; isCallingUidPersistentSystemProcess: ")
.append(mIsCallingUidPersistentSystemProcess);
sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
- if (!isPendingIntent()) {
+ if (isPendingIntent()) {
sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
sb.append("; realCallingPackage: ")
.append(getDebugPackageName(mRealCallingPackage, mRealCallingUid));
@@ -340,16 +346,18 @@
ActivityManager.class, "PROCESS_STATE_", mRealCallingUidProcState));
sb.append("; isRealCallingUidPersistentSystemProcess: ")
.append(mIsRealCallingUidPersistentSystemProcess);
+ sb.append("; originatingPendingIntent: ").append(mOriginatingPendingIntent);
}
- sb.append("; originatingPendingIntent: ").append(mOriginatingPendingIntent);
- sb.append("; backgroundStartPrivileges: ").append(mBackgroundStartPrivileges);
+ sb.append("; mForcedBalByPiSender: ").append(mForcedBalByPiSender);
sb.append("; intent: ").append(mIntent);
sb.append("; callerApp: ").append(mCallerApp);
- sb.append("; realCallerApp: ").append(mRealCallerApp);
+ if (isPendingIntent()) {
+ sb.append("; realCallerApp: ").append(mRealCallerApp);
+ }
if (mCallerApp != null) {
sb.append("; inVisibleTask: ").append(mCallerApp.hasActivityInVisibleTask());
}
- if (!isPendingIntent()) {
+ if (isPendingIntent()) {
if (mRealCallerApp != null) {
sb.append("; realInVisibleTask: ")
.append(mRealCallerApp.hasActivityInVisibleTask());
@@ -418,8 +426,26 @@
}
/**
- * @return A code denoting which BAL rule allows an activity to be started,
- * or {@link #BAL_BLOCK} if the launch should be blocked
+ * Check if a (background) activity start is allowed.
+ *
+ * @param callingUid The UID that wants to start the activity.
+ * @param callingPid The PID that wants to start the activity.
+ * @param callingPackage The package name that wants to start the activity.
+ * @param realCallingUid The UID that actually calls this method (only if this handles a
+ * PendingIntent, otherwise -1)
+ * @param realCallingPid The PID that actually calls this method (only if this handles a
+ * * PendingIntent, otherwise -1)
+ * @param callerApp The process that calls this method (only if not a PendingIntent)
+ * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+ * null if not originated by PendingIntent
+ * @param forcedBalByPiSender If set to allow, the
+ * PendingIntent's sender will try to force allow background activity starts.
+ * This is only possible if the sender of the PendingIntent is a system process.
+ * @param intent Intent that should be started.
+ * @param checkedOptions ActivityOptions to allow specific opt-ins/opt outs.
+ *
+ * @return A verdict denoting which BAL rule allows an activity to be started,
+ * or if the launch should be blocked.
*/
BalVerdict checkBackgroundActivityStart(
int callingUid,
@@ -429,7 +455,7 @@
int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges backgroundStartPrivileges,
+ BackgroundStartPrivileges forcedBalByPiSender,
Intent intent,
ActivityOptions checkedOptions) {
@@ -440,7 +466,7 @@
BalState state = new BalState(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
- backgroundStartPrivileges, intent, checkedOptions);
+ forcedBalByPiSender, intent, checkedOptions);
// In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
// visible window.
@@ -469,7 +495,7 @@
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity launch blocked! "
+ state.dump(resultForCaller));
- showBalToast("BAL blocked", state);
+ showBalBlockedToast("BAL blocked", state);
return statsLog(BalVerdict.BLOCK, state);
}
@@ -509,10 +535,12 @@
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
// Both caller and real caller allow with system defined behavior
Slog.wtf(TAG,
- "With Android 15 BAL hardening this activity start would be blocked"
+ "With Android 15 BAL hardening this activity start may be blocked"
+ + " if the PI creator upgrades target_sdk to 35+"
+ + " AND the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalToast("BAL would be blocked", state);
+ showBalRiskToast("BAL would be blocked", state);
// return the realCaller result for backwards compatibility
return statsLog(resultForRealCaller, state);
}
@@ -521,10 +549,11 @@
== ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
// Allowed before V by creator
Slog.wtf(TAG,
- "With Android 15 BAL hardening this activity start would be blocked"
+ "With Android 15 BAL hardening this activity start may be blocked"
+ + " if the PI creator upgrades target_sdk to 35+! "
+ " (missing opt in by PI creator)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalToast("BAL would be blocked", state);
+ showBalRiskToast("BAL would be blocked", state);
return statsLog(resultForCaller, state);
}
if (resultForRealCaller.allows()
@@ -533,10 +562,11 @@
// Allowed before U by sender
if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
Slog.wtf(TAG,
- "With Android 14 BAL hardening this activity start would be blocked"
+ "With Android 14 BAL hardening this activity start will be blocked"
+ + " if the PI sender upgrades target_sdk to 34+! "
+ " (missing opt in by PI sender)! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalToast("BAL would be blocked", state);
+ showBalBlockedToast("BAL would be blocked", state);
return statsLog(resultForRealCaller, state);
}
Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
@@ -547,7 +577,7 @@
// anything that has fallen through would currently be aborted
Slog.w(TAG, "Background activity launch blocked! "
+ state.dump(resultForCaller, resultForRealCaller));
- showBalToast("BAL blocked", state);
+ showBalBlockedToast("BAL blocked", state);
return statsLog(BalVerdict.BLOCK, state);
}
@@ -700,12 +730,12 @@
}
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
- if (state.mIsRealCallingUidPersistentSystemProcess
- && state.mBackgroundStartPrivileges.allowsBackgroundActivityStarts()) {
+ if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
+ && state.mIsRealCallingUidPersistentSystemProcess) {
return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
/*background*/ false,
"realCallingUid is persistent system process AND intent "
- + "sender allowed (allowBackgroundActivityStart = true).");
+ + "sender forced to allow.");
}
// don't abort if the realCallingUid is an associated companion app
if (mService.isAssociatedCompanionApp(
@@ -922,7 +952,15 @@
return true;
}
- private void showBalToast(String toastText, BalState state) {
+ private void showBalBlockedToast(String toastText, BalState state) {
+ if (balShowToastsBlocked()) {
+ showToast(toastText
+ + " caller:" + state.mCallingPackage
+ + " realCaller:" + state.mRealCallingPackage);
+ }
+ }
+
+ private void showBalRiskToast(String toastText, BalState state) {
if (balShowToasts()) {
showToast(toastText
+ " caller:" + state.mCallingPackage
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index d69c5ef..8a7cc67 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1876,7 +1876,8 @@
dc.getDisplayPolicy().simulateLayoutDisplay(df);
final InsetsState insetsState = df.mInsetsState;
final Rect displayFrame = insetsState.getDisplayFrame();
- final Insets decor = insetsState.calculateInsets(displayFrame, DECOR_TYPES,
+ final Insets decor = insetsState.calculateInsets(displayFrame,
+ dc.mWmService.mDecorTypes,
true /* ignoreVisibility */);
final Insets statusBar = insetsState.calculateInsets(displayFrame,
Type.statusBars(), true /* ignoreVisibility */);
@@ -1912,15 +1913,6 @@
}
}
-
- static final int DECOR_TYPES = Type.displayCutout() | Type.navigationBars();
-
- /**
- * The types that may affect display configuration. This excludes cutout because it is
- * known from display info.
- */
- static final int CONFIG_TYPES = Type.statusBars() | Type.navigationBars();
-
private final DisplayContent mDisplayContent;
private final Info[] mInfoForRotation = new Info[4];
final Info mTmpInfo = new Info();
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cd114fc..9d5ddf3 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -30,6 +30,7 @@
import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING;
import static com.android.server.wm.InsetsSourceProviderProto.SERVER_VISIBLE;
import static com.android.server.wm.InsetsSourceProviderProto.SOURCE;
+import static com.android.server.wm.InsetsSourceProviderProto.SOURCE_WINDOW_STATE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
import android.annotation.NonNull;
@@ -304,7 +305,7 @@
return mInsetsHint;
}
final WindowState win = mWindowContainer.asWindowState();
- if (win != null && win.mGivenInsetsPending) {
+ if (win != null && win.mGivenInsetsPending && win.mAttrs.providedInsets == null) {
return mInsetsHint;
}
if (mInsetsHintStale) {
@@ -680,6 +681,9 @@
proto.write(SERVER_VISIBLE, mServerVisible);
proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
proto.write(CONTROLLABLE, mControllable);
+ if (mWindowContainer != null && mWindowContainer.asWindowState() != null) {
+ mWindowContainer.asWindowState().dumpDebug(proto, SOURCE_WINDOW_STATE, logLevel);
+ }
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e82f322..5227a52 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2095,6 +2095,7 @@
}
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
+ final TaskFragment taskFragment = r.getTaskFragment();
final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
if (singleActivity) {
rootTask = task;
@@ -2137,7 +2138,11 @@
.setIntent(r.intent)
.setDeferTaskAppear(true)
.setHasBeenVisible(true)
- .setWindowingMode(task.getRequestedOverrideWindowingMode())
+ // In case the activity is in system split screen, or Activity Embedding
+ // split, we need to animate the PIP Task from the original TaskFragment
+ // bounds, so also setting the windowing mode, otherwise the bounds may
+ // be reset to fullscreen.
+ .setWindowingMode(taskFragment.getWindowingMode())
.build();
// Establish bi-directional link between the original and pinned task.
r.setLastParentBeforePip(launchIntoPipHostActivity);
@@ -2150,7 +2155,7 @@
// current bounds.
// Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
// will be updated later after this is collected in transition.
- rootTask.setBoundsUnchecked(r.getTaskFragment().getBounds());
+ rootTask.setBoundsUnchecked(taskFragment.getBounds());
// Move the last recents animation transaction from original task to the new one.
if (task.mLastRecentsAnimationTransaction != null) {
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index f4f641f..bffdf54 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -23,7 +23,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
-import android.annotation.TestApi;
import android.graphics.Bitmap;
import android.os.Process;
import android.os.SystemClock;
@@ -33,6 +32,7 @@
import android.window.TaskSnapshot;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
@@ -100,7 +100,7 @@
}
}
- @TestApi
+ @VisibleForTesting
void waitForQueueEmpty() {
while (true) {
synchronized (mLock) {
@@ -112,9 +112,20 @@
}
}
- @GuardedBy("mLock")
- void sendToQueueLocked(WriteQueueItem item) {
- mWriteQueue.offer(item);
+ @VisibleForTesting
+ int peekQueueSize() {
+ synchronized (mLock) {
+ return mWriteQueue.size();
+ }
+ }
+
+ private void addToQueueInternal(WriteQueueItem item, boolean insertToFront) {
+ mWriteQueue.removeFirstOccurrence(item);
+ if (insertToFront) {
+ mWriteQueue.addFirst(item);
+ } else {
+ mWriteQueue.addLast(item);
+ }
item.onQueuedLocked();
ensureStoreQueueDepthLocked();
if (!mPaused) {
@@ -123,6 +134,16 @@
}
@GuardedBy("mLock")
+ void sendToQueueLocked(WriteQueueItem item) {
+ addToQueueInternal(item, false /* insertToFront */);
+ }
+
+ @GuardedBy("mLock")
+ void insertQueueAtFirstLocked(WriteQueueItem item) {
+ addToQueueInternal(item, true /* insertToFront */);
+ }
+
+ @GuardedBy("mLock")
private void ensureStoreQueueDepthLocked() {
while (mStoreQueueItems.size() > MAX_STORE_QUEUE_DEPTH) {
final StoreWriteQueueItem item = mStoreQueueItems.poll();
@@ -235,6 +256,8 @@
@GuardedBy("mLock")
@Override
void onQueuedLocked() {
+ // Remove duplicate request.
+ mStoreQueueItems.remove(this);
mStoreQueueItems.offer(this);
}
@@ -358,6 +381,14 @@
return true;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) return false;
+ final StoreWriteQueueItem other = (StoreWriteQueueItem) o;
+ return mId == other.mId && mUserId == other.mUserId
+ && mPersistInfoProvider == other.mPersistInfoProvider;
+ }
}
DeleteWriteQueueItem createDeleteWriteQueueItem(int id, int userId,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 00f2b89..197edc3 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -274,11 +274,6 @@
boolean mClearedForReorderActivityToFront;
/**
- * Whether the TaskFragment surface is managed by a system {@link TaskFragmentOrganizer}.
- */
- boolean mIsSurfaceManagedBySystemOrganizer = false;
-
- /**
* When we are in the process of pausing an activity, before starting the
* next one, this variable holds the activity that is currently being paused.
*
@@ -453,21 +448,13 @@
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@NonNull String processName) {
- setTaskFragmentOrganizer(organizer, uid, processName,
- false /* isSurfaceManagedBySystemOrganizer */);
- }
-
- void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
- @NonNull String processName, boolean isSurfaceManagedBySystemOrganizer) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
mTaskFragmentOrganizerUid = uid;
mTaskFragmentOrganizerProcessName = processName;
- mIsSurfaceManagedBySystemOrganizer = isSurfaceManagedBySystemOrganizer;
}
void onTaskFragmentOrganizerRemoved() {
mTaskFragmentOrganizer = null;
- mIsSurfaceManagedBySystemOrganizer = false;
}
/** Whether this TaskFragment is organized by the given {@code organizer}. */
@@ -1548,10 +1535,11 @@
next.getTask().mTaskId, next.shortComponentName);
mAtmService.getAppWarningsLocked().onResumeActivity(next);
- next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+ final int topProcessState = mAtmService.mTopProcessState;
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState);
next.abortAndClearOptionsAnimation();
transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.token, next.app.getReportedProcState(),
+ ResumeActivityItem.obtain(next.token, topProcessState,
dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus()));
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
@@ -2454,9 +2442,6 @@
if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) {
return;
}
- if (mIsSurfaceManagedBySystemOrganizer) {
- return;
- }
if (mTransitionController.isShellTransitionsEnabled()
&& !mTransitionController.isCollecting(this)) {
// TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index b23ffa8..c0bf2ce 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -59,6 +59,7 @@
import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
@@ -1272,18 +1273,23 @@
}
}
// Commit wallpaper visibility after activity, because usually the wallpaper target token is
- // an activity, and wallpaper's visibility is depends on activity's visibility.
+ // an activity, and wallpaper's visibility depends on activity's visibility.
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
if (wt == null) continue;
final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget();
final boolean isTargetInvisible = target == null || !target.mToken.isVisible();
- if (isTargetInvisible || (!wt.isVisibleRequested()
- && !mVisibleAtTransitionEndTokens.contains(wt))) {
+ final boolean isWallpaperVisibleAtEnd =
+ wt.isVisibleRequested() || mVisibleAtTransitionEndTokens.contains(wt);
+ if (isTargetInvisible || !isWallpaperVisibleAtEnd) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" Commit wallpaper becoming invisible: %s", wt);
wt.commitVisibility(false /* visible */);
}
+ if (isTargetInvisible) {
+ // Our original target went invisible, so we should look for a new target.
+ wt.mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
}
if (committedSomeInvisible) {
mController.onCommittedInvisibles();
@@ -1408,12 +1414,13 @@
false /* forceRelayout */);
}
cleanUpInternal();
- mController.updateAnimatingState();
// Handle back animation if it's already started.
mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this);
mController.mFinishingTransition = null;
mController.mSnapshotController.onTransitionFinish(mType, mTargets);
+ // Resume snapshot persist thread after snapshot controller analysis this transition.
+ mController.updateAnimatingState();
}
@Nullable
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6e3d24b..ab3ddbd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -336,6 +336,7 @@
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
+import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
@@ -542,6 +543,12 @@
@VisibleForTesting
boolean mSkipActivityRelaunchWhenDocking;
+ /** Device default insets types provided non-decor insets. */
+ final int mDecorTypes;
+
+ /** Device default insets types shall be excluded from config app sizes. */
+ final int mConfigTypes;
+
final boolean mLimitedAlphaCompositing;
final int mMaxUiWidth;
@@ -1185,6 +1192,16 @@
com.android.internal.R.bool.config_assistantOnTopOfDream);
mSkipActivityRelaunchWhenDocking = context.getResources()
.getBoolean(R.bool.config_skipActivityRelaunchWhenDocking);
+ final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources()
+ .getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize)
+ && Flags.closeToSquareConfigIncludesStatusBar();
+ if (!isScreenSizeDecoupledFromStatusBarAndCutout) {
+ mDecorTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars();
+ mConfigTypes = WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars();
+ } else {
+ mDecorTypes = WindowInsets.Type.navigationBars();
+ mConfigTypes = WindowInsets.Type.navigationBars();
+ }
mLetterboxConfiguration = new LetterboxConfiguration(
// Using SysUI context to have access to Material colors extracted from Wallpaper.
@@ -7319,6 +7336,7 @@
}
}
+ /** This is used when there's no app info available and shall return the system default.*/
void getStableInsetsLocked(int displayId, Rect outInsets) {
outInsets.setEmpty();
final DisplayContent dc = mRoot.getDisplayContent(displayId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 95e2515..89d47bc 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -2122,8 +2122,7 @@
// actions.
TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
taskFragment.setTaskFragmentOrganizer(organizerToken,
- ownerActivity.getUid(), ownerActivity.info.processName,
- mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder()));
+ ownerActivity.getUid(), ownerActivity.info.processName);
final int position;
if (creationParams.getPairedPrimaryFragmentToken() != null) {
// When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ae2df00..4e17011 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1381,7 +1381,7 @@
// This window doesn't provide any insets.
return;
}
- if (mGivenInsetsPending) {
+ if (mGivenInsetsPending && mAttrs.providedInsets == null) {
// The given insets are pending, and they are not reliable for now. The source frame
// should be updated after the new given insets are sent to window manager.
return;
@@ -1829,7 +1829,7 @@
}
for (int i = mInsetsSourceProviders.size() - 1; i >= 0; i--) {
final InsetsSource source = mInsetsSourceProviders.valueAt(i).getSource();
- if ((source.getType() & DisplayPolicy.DecorInsets.CONFIG_TYPES) != 0) {
+ if ((source.getType() & mWmService.mConfigTypes) != 0) {
return true;
}
}
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index dc05462..80427b3 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -25,7 +25,7 @@
mThiz = env->NewWeakGlobalRef(thiz);
mTvInput = tvInput;
mLooper = looper;
- mTvInputCallback = ::ndk::SharedRefBase::make<TvInputCallback>(this);
+ mTvInputCallback = std::shared_ptr<TvInputCallbackWrapper>(new TvInputCallbackWrapper(this));
mTvInput->setCallback(mTvInputCallback);
}
@@ -443,18 +443,23 @@
}
}
-JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
+JTvInputHal::TvInputCallbackWrapper::TvInputCallbackWrapper(JTvInputHal* hal) {
+ aidlTvInputCallback = ::ndk::SharedRefBase::make<AidlTvInputCallback>(hal);
+ hidlTvInputCallback = sp<HidlTvInputCallback>::make(hal);
+}
+
+JTvInputHal::AidlTvInputCallback::AidlTvInputCallback(JTvInputHal* hal) {
mHal = hal;
}
-::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notify(const AidlTvInputEvent& event) {
+::ndk::ScopedAStatus JTvInputHal::AidlTvInputCallback::notify(const AidlTvInputEvent& event) {
mHal->mLooper->sendMessage(new NotifyHandler(mHal,
TvInputEventWrapper::createEventWrapper(event)),
static_cast<int>(event.type));
return ::ndk::ScopedAStatus::ok();
}
-::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notifyTvMessageEvent(
+::ndk::ScopedAStatus JTvInputHal::AidlTvInputCallback::notifyTvMessageEvent(
const AidlTvMessageEvent& event) {
const std::string DEVICE_ID_SUBTYPE = "device_id";
::ndk::ScopedAStatus status = ::ndk::ScopedAStatus::ok();
@@ -487,11 +492,14 @@
: mIsHidl(false), mAidlTvInput(aidlTvInput) {}
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback(
- const std::shared_ptr<TvInputCallback>& in_callback) {
+ const std::shared_ptr<TvInputCallbackWrapper>& in_callback) {
if (mIsHidl) {
- return hidlSetCallback(in_callback);
+ in_callback->aidlTvInputCallback = nullptr;
+ return hidlSetCallback(in_callback == nullptr ? nullptr : in_callback->hidlTvInputCallback);
} else {
- return mAidlTvInput->setCallback(in_callback);
+ in_callback->hidlTvInputCallback = nullptr;
+ return mAidlTvInput->setCallback(in_callback == nullptr ? nullptr
+ : in_callback->aidlTvInputCallback);
}
}
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index 6026a10..2ef94ac 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -168,23 +168,39 @@
JTvInputHal* mHal;
};
- class TvInputCallback : public HidlITvInputCallback, public BnTvInputCallback {
+ class AidlTvInputCallback : public BnTvInputCallback {
public:
- explicit TvInputCallback(JTvInputHal* hal);
+ explicit AidlTvInputCallback(JTvInputHal* hal);
::ndk::ScopedAStatus notify(const AidlTvInputEvent& event) override;
::ndk::ScopedAStatus notifyTvMessageEvent(const AidlTvMessageEvent& event) override;
+
+ private:
+ JTvInputHal* mHal;
+ };
+
+ class HidlTvInputCallback : public HidlITvInputCallback {
+ public:
+ explicit HidlTvInputCallback(JTvInputHal* hal);
Return<void> notify(const HidlTvInputEvent& event) override;
private:
JTvInputHal* mHal;
};
+ class TvInputCallbackWrapper {
+ public:
+ explicit TvInputCallbackWrapper(JTvInputHal* hal);
+ std::shared_ptr<AidlTvInputCallback> aidlTvInputCallback;
+ sp<HidlTvInputCallback> hidlTvInputCallback;
+ };
+
class ITvInputWrapper {
public:
ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput);
ITvInputWrapper(sp<HidlITvInput>& hidlTvInput);
- ::ndk::ScopedAStatus setCallback(const std::shared_ptr<TvInputCallback>& in_callback);
+ ::ndk::ScopedAStatus setCallback(
+ const std::shared_ptr<TvInputCallbackWrapper>& in_callback);
::ndk::ScopedAStatus getStreamConfigurations(int32_t in_deviceId,
std::vector<AidlTvStreamConfig>* _aidl_return);
::ndk::ScopedAStatus openStream(int32_t in_deviceId, int32_t in_streamId,
@@ -198,7 +214,7 @@
::ndk::ScopedAStatus getAidlInterfaceVersion(int32_t* _aidl_return);
private:
- ::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback);
+ ::ndk::ScopedAStatus hidlSetCallback(const sp<HidlTvInputCallback>& in_callback);
::ndk::ScopedAStatus hidlGetStreamConfigurations(
int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return);
::ndk::ScopedAStatus hidlOpenStream(int32_t in_deviceId, int32_t in_streamId,
@@ -229,7 +245,7 @@
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
std::shared_ptr<ITvInputWrapper> mTvInput;
- std::shared_ptr<TvInputCallback> mTvInputCallback;
+ std::shared_ptr<TvInputCallbackWrapper> mTvInputCallback;
};
} // namespace android
diff --git a/services/core/jni/tvinput/TvInputHal_hidl.cpp b/services/core/jni/tvinput/TvInputHal_hidl.cpp
index 37cf844..cdd9266 100644
--- a/services/core/jni/tvinput/TvInputHal_hidl.cpp
+++ b/services/core/jni/tvinput/TvInputHal_hidl.cpp
@@ -59,7 +59,11 @@
return event;
}
-Return<void> JTvInputHal::TvInputCallback::notify(const HidlTvInputEvent& event) {
+JTvInputHal::HidlTvInputCallback::HidlTvInputCallback(JTvInputHal* hal) {
+ mHal = hal;
+}
+
+Return<void> JTvInputHal::HidlTvInputCallback::notify(const HidlTvInputEvent& event) {
mHal->mLooper->sendMessage(new NotifyHandler(mHal,
TvInputEventWrapper::createEventWrapper(event)),
static_cast<int>(event.type));
@@ -70,9 +74,8 @@
: mIsHidl(true), mHidlTvInput(hidlTvInput) {}
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlSetCallback(
- const std::shared_ptr<TvInputCallback>& in_callback) {
- mHidlTvInput->setCallback(in_callback == nullptr ? nullptr
- : sp<TvInputCallback>(in_callback.get()));
+ const sp<HidlTvInputCallback>& in_callback) {
+ mHidlTvInput->setCallback(in_callback);
return ::ndk::ScopedAStatus::ok();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 93dc219..34d6755 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2008,6 +2008,10 @@
DeviceManagementResourcesProvider getDeviceManagementResourcesProvider() {
return new DeviceManagementResourcesProvider();
}
+
+ boolean isAdminInstalledCaCertAutoApproved() {
+ return false;
+ }
}
/**
@@ -6158,6 +6162,18 @@
.setAdmin(caller.getPackageName())
.setBoolean(/* isDelegate */ admin == null)
.write();
+
+ if (mInjector.isAdminInstalledCaCertAutoApproved()
+ && installedAlias != null
+ && admin != null) {
+ // If device admin called this, approve cert to avoid notifications
+ Slogf.i(LOG_TAG, "Approving admin installed cert");
+ approveCaCert(
+ installedAlias,
+ caller.getUserId(),
+ /* approved */ true);
+ }
+
return installedAlias;
});
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 56e385d..0a2e806 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -129,6 +129,7 @@
import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.coverage.CoverageService;
import com.android.server.cpu.CpuMonitorService;
+import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.devicestate.DeviceStateManagerService;
import com.android.server.display.DisplayManagerService;
@@ -964,6 +965,7 @@
// Only update the timeout after starting all the services so that we use
// the default timeout to start system server.
updateWatchdogTimeout(t);
+ CriticalEventLog.getInstance().logSystemServerStarted();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 7ceccc5..47ae97f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -44,7 +44,6 @@
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.usage.UsageStatsManager;
-import android.content.AttributionSourceState;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -230,20 +229,12 @@
private AppStateTrackerTestable newInstance() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mMockIAppOpsService.checkOperationWithState(eq(TARGET_OP), any()))
- .thenAnswer(
- (Answer<Integer>)
- invocation -> {
- AttributionSourceState attribution =
- (AttributionSourceState) invocation.getArguments()[1];
- return mRestrictedPackages.indexOf(
- Pair.create(
- attribution.uid,
- attribution.packageName))
- >= 0
- ? AppOpsManager.MODE_IGNORED
- : AppOpsManager.MODE_ALLOWED;
- });
+ when(mMockIAppOpsService.checkOperation(eq(TARGET_OP), anyInt(), anyString()))
+ .thenAnswer(inv -> {
+ return mRestrictedPackages.indexOf(
+ Pair.create(inv.getArgument(1), inv.getArgument(2))) >= 0 ?
+ AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
+ });
final AppStateTrackerTestable instance = new AppStateTrackerTestable();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 032d026..2f909f8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -69,7 +69,6 @@
import android.app.IApplicationThread;
import android.app.IUidObserver;
import android.app.SyncNotedAppOp;
-import android.content.AttributionSourceState;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -234,16 +233,12 @@
assertThat(sProcessListSettingsListener).isNotNull();
}
- private void mockNoteOp() {
+ private void mockNoteOperation() {
SyncNotedAppOp allowed = new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED,
AppOpsManager.OP_GET_USAGE_STATS, null, mContext.getPackageName());
- when(mAppOpsService.noteOperationWithState(
- eq(AppOpsManager.OP_GET_USAGE_STATS),
- any(AttributionSourceState.class),
- any(Boolean.class),
- nullable(String.class),
- any(Boolean.class)))
- .thenReturn(allowed);
+ when(mAppOpsService.noteOperation(eq(AppOpsManager.OP_GET_USAGE_STATS), eq(Process.myUid()),
+ nullable(String.class), nullable(String.class), any(Boolean.class),
+ nullable(String.class), any(Boolean.class))).thenReturn(allowed);
}
@After
@@ -696,7 +691,7 @@
*/
@Test
public void testDispatchUids_dispatchNeededChanges() throws RemoteException {
- mockNoteOp();
+ mockNoteOperation();
final int[] changesToObserve = {
ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -905,7 +900,7 @@
*/
@Test
public void testDispatchUidChanges_procStateCutpoint() throws RemoteException {
- mockNoteOp();
+ mockNoteOperation();
final IUidObserver observer = mock(IUidObserver.Stub.class);
@@ -975,7 +970,7 @@
*/
@Test
public void testDispatchUidChanges_validateUidsUpdated() {
- mockNoteOp();
+ mockNoteOperation();
final int[] changesForPendingItems = UID_RECORD_CHANGES;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index dcbee83..bb91939 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -113,8 +113,6 @@
import android.app.NotificationManager;
import android.app.role.RoleManager;
import android.app.usage.AppStandbyInfo;
-import android.content.AttributionSource;
-import android.content.AttributionSourceState;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -2456,12 +2454,9 @@
doReturn(granted ? MODE_ALLOWED : MODE_IGNORED)
.when(mAppOpsManager)
.checkOpNoThrow(op, uid, packageName);
- AttributionSource attributionSource =
- new AttributionSource.Builder(uid).setPackageName(packageName).build();
- AttributionSourceState attributionSourceState = attributionSource.asState();
doReturn(granted ? MODE_ALLOWED : MODE_IGNORED)
.when(mIAppOpsService)
- .checkOperationWithState(eq(op), eq(attributionSourceState));
+ .checkOperation(op, uid, packageName);
} catch (RemoteException e) {
// Ignore.
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index daed5df..646f486 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -49,7 +49,6 @@
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
-import android.content.AttributionSource;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
@@ -217,21 +216,18 @@
}
@Test
- public void testNoteOpAndGetOpsForPackage() {
+ public void testNoteOperationAndGetOpsForPackage() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
// Note an op that's allowed.
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
// Note another op that's not allowed.
- mAppOpsService.noteOperationWithState(OP_WRITE_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null,
+ false);
loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED);
@@ -243,24 +239,20 @@
* ACCESS_COARSE_LOCATION op is used to check whether WIFI_SCAN is allowed.
*/
@Test
- public void testNoteOpAndGetOpsForPackage_controlledByDifferentOp() {
+ public void testNoteOperationAndGetOpsForPackage_controlledByDifferentOp() {
// This op controls WIFI_SCAN
mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED);
- assertThat(mAppOpsService.noteOperationWithState(OP_WIFI_SCAN,
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName)
- .build().asState(), false, null, false).getOpMode())
- .isEqualTo(MODE_ALLOWED);
+ assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
+ null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, -1,
MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
// Now set COARSE_LOCATION to ERRORED -> this will make WIFI_SCAN disabled as well.
mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED);
- assertThat(mAppOpsService.noteOperationWithState(OP_WIFI_SCAN,
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName)
- .build().asState(), false, null, false)
- .getOpMode()).isEqualTo(MODE_ERRORED);
+ assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
+ null, false).getOpMode()).isEqualTo(MODE_ERRORED);
assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, mTestStartMillis,
MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
@@ -271,12 +263,9 @@
public void testStatePersistence() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
- null, false);
- mAppOpsService.noteOperationWithState(OP_WRITE_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+ mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null,
+ false);
mAppOpsService.shutdown();
@@ -294,10 +283,7 @@
@Test
public void testShutdown() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
mAppOpsService.shutdown();
// Create a new app ops service which will initialize its state from XML.
@@ -311,10 +297,7 @@
@Test
public void testGetOpsForPackage() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
// Query all ops
List<PackageOps> loggedOps = mAppOpsService.getOpsForPackage(
@@ -343,10 +326,7 @@
@Test
public void testPackageRemoved() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -361,8 +341,7 @@
@Test
public void testPackageRemovedHistoricalOps() throws InterruptedException {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- mAppOpsService.noteOperationWithState(OP_READ_SMS, mMyUid, sMyPackageName, null, false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000);
historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName, null,
@@ -402,10 +381,7 @@
@Test
public void testUidRemoved() {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(),
- false, null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
List<PackageOps> loggedOps = getLoggedOps();
assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -417,10 +393,7 @@
@Test
public void testUidStateInitializationDoesntClearState() throws InterruptedException {
mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
- AttributionSource attributionSource =
- new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
- mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
- null, false);
+ mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
mAppOpsService.initializeUidStates();
List<PackageOps> ops = mAppOpsService.getOpsForPackage(mMyUid, sMyPackageName,
new int[]{OP_READ_SMS});
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index fe31b9c..a776eec 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -267,7 +267,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DIM);
@@ -305,7 +306,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE);
@@ -342,7 +344,8 @@
/* dozeAfterScreenOff= */ true,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
@@ -378,7 +381,8 @@
/* dozeAfterScreenOff= */ true,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
@@ -414,7 +418,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
@@ -451,7 +456,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
@@ -486,7 +492,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ false,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
@@ -522,7 +529,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ false,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
@@ -557,7 +565,8 @@
/* dozeAfterScreenOff= */ false,
/* bootCompleted= */ true,
/* screenBrightnessBoostInProgress= */ true,
- /* waitForNegativeProximity= */ false);
+ /* waitForNegativeProximity= */ false,
+ /* brightWhenDozing= */ false);
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mPowerGroup.mDisplayPowerRequest;
assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index c88d6e4..612a091 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -29,6 +29,9 @@
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -40,6 +43,7 @@
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.utils.TouchEventGenerator;
import org.junit.After;
@@ -59,20 +63,29 @@
@RunWith(AndroidJUnit4.class)
public class WindowMagnificationGestureHandlerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
public static final int STATE_IDLE = 1;
public static final int STATE_SHOW_MAGNIFIER_SHORTCUT = 2;
public static final int STATE_TWO_FINGERS_DOWN = 3;
public static final int STATE_SHOW_MAGNIFIER_TRIPLE_TAP = 4;
public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 5;
public static final int STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD = 6;
+ public static final int STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP = 7;
+ public static final int STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD = 8;
+ public static final int STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD = 9;
//TODO: Test it after can injecting Handler to GestureMatcher is available.
public static final int FIRST_STATE = STATE_IDLE;
public static final int LAST_STATE = STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD;
+ public static final int LAST_STATE_WITH_MULTI_FINGER =
+ STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_TAP_X = 301;
public static final float DEFAULT_TAP_Y = 299;
+ public static final PointF DEFAULT_POINT = new PointF(DEFAULT_TAP_X, DEFAULT_TAP_Y);
private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY;
@Rule
@@ -141,7 +154,22 @@
throw new AssertionError("Failed while testing state " + stateToString(state),
e);
}
- });
+ }, LAST_STATE);
+ }
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testEachState_enabledMultiFinger_isReachableAndRecoverable() {
+ forEachState(state -> {
+ goFromStateIdleTo(state);
+ assertIn(state);
+ returnToNormalFrom(state);
+ try {
+ assertIn(STATE_IDLE);
+ } catch (AssertionError e) {
+ throw new AssertionError("Failed while testing state " + stateToString(state),
+ e);
+ }
+ }, LAST_STATE_WITH_MULTI_FINGER);
}
@Test
@@ -159,8 +187,29 @@
returnToNormalFrom(state1);
}
}
- });
- });
+ }, LAST_STATE);
+ }, LAST_STATE);
+ }
+
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+ public void testStates_enabledMultiFinger_areMutuallyExclusive() {
+ forEachState(state1 -> {
+ forEachState(state2 -> {
+ if (state1 < state2) {
+ goFromStateIdleTo(state1);
+ try {
+ assertIn(state2);
+ fail("State " + stateToString(state1) + " also implies state "
+ + stateToString(state2) + stateDump());
+ } catch (AssertionError e) {
+ // expected
+ returnToNormalFrom(state1);
+ }
+ }
+ }, LAST_STATE_WITH_MULTI_FINGER);
+ }, LAST_STATE_WITH_MULTI_FINGER);
}
@Test
@@ -187,8 +236,8 @@
returnToNormalFrom(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
}
- private void forEachState(IntConsumer action) {
- for (int state = FIRST_STATE; state <= LAST_STATE; state++) {
+ private void forEachState(IntConsumer action, int lastState) {
+ for (int state = FIRST_STATE; state <= lastState; state++) {
action.accept(state);
}
}
@@ -207,14 +256,16 @@
}
break;
case STATE_SHOW_MAGNIFIER_SHORTCUT:
- case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: {
+ case STATE_SHOW_MAGNIFIER_TRIPLE_TAP:
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP:
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mDetectingState, state);
- }
break;
case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
- case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
check(isWindowMagnifierEnabled(DISPLAY_0), state);
check(mWindowMagnificationGestureHandler.mCurrentState
== mWindowMagnificationGestureHandler.mViewportDraggingState, state);
@@ -286,6 +337,29 @@
tapAndHold();
}
break;
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP: {
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+ // Wait for two-finger tap gesture completed.
+ SystemClock.sleep(ViewConfiguration.getDoubleTapMinTime() + 500);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+ break;
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTapAndHold();
+ }
+ break;
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD: {
+ // enabled then perform two finger triple tap and hold gesture
+ goFromStateIdleTo(STATE_SHOW_MAGNIFIER_SHORTCUT);
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTapAndHold();
+ }
+ break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -319,13 +393,22 @@
tap();
}
break;
- case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
+ case STATE_NOT_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
- }
- break;
- case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD: {
+ break;
+ case STATE_ENABLED_SHOW_MAGNIFIER_TRIPLE_TAP_AND_HOLD:
+ case STATE_ENABLED_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP_AND_HOLD:
send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
returnToNormalFrom(STATE_SHOW_MAGNIFIER_SHORTCUT);
+ break;
+ case STATE_SHOW_MAGNIFIER_TWO_FINGER_TRIPLE_TAP: {
+ twoFingerTap();
+ twoFingerTap();
+ twoFingerTap();
+ // Wait for two-finger tap gesture completed.
+ SystemClock.sleep(ViewConfiguration.getDoubleTapMinTime() + 500);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
break;
default:
@@ -365,6 +448,16 @@
return TouchEventGenerator.downEvent(DISPLAY_0, x, y);
}
+ private MotionEvent pointerDownEvent(float x, float y) {
+ return TouchEventGenerator.pointerDownEvent(DISPLAY_0,
+ new PointF[] {DEFAULT_POINT, new PointF(x, y)});
+ }
+
+ private MotionEvent pointerUpEvent(float x, float y) {
+ return TouchEventGenerator.pointerUpEvent(DISPLAY_0,
+ new PointF[] {DEFAULT_POINT, new PointF(x, y)});
+ }
+
private MotionEvent upEvent(float x, float y) {
return TouchEventGenerator.upEvent(DISPLAY_0, x, y);
}
@@ -379,6 +472,19 @@
SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100);
}
+ private void twoFingerTap() {
+ send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ send(pointerDownEvent(DEFAULT_TAP_X * 2, DEFAULT_TAP_Y));
+ send(pointerUpEvent(DEFAULT_TAP_X * 2, DEFAULT_TAP_Y));
+ send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ }
+
+ private void twoFingerTapAndHold() {
+ send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y));
+ send(pointerDownEvent(DEFAULT_TAP_X * 2, DEFAULT_TAP_Y));
+ SystemClock.sleep(ViewConfiguration.getLongPressTimeout() + 100);
+ }
+
private String stateDump() {
return "\nCurrent state dump:\n" + mWindowMagnificationGestureHandler.mCurrentState;
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
index fbcde53..fcd16a0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java
@@ -39,10 +39,36 @@
return generateSingleTouchEvent(displayId, ACTION_DOWN, x, y);
}
+ /**
+ * Create a test {@link MotionEvent#ACTION_POINTER_DOWN}, filling in all the basic values that
+ * define the motion.
+ *
+ * @param displayId The id of the display
+ * @param pointFs location on the screen of the all pointers
+ */
+ public static MotionEvent pointerDownEvent(int displayId, PointF[] pointFs) {
+ final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int action = ACTION_POINTER_DOWN | actionIndex;
+ return generateMultiplePointersEvent(displayId, action, pointFs);
+ }
+
public static MotionEvent moveEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_MOVE, x, y);
}
+ /**
+ * Create a test {@link MotionEvent#ACTION_POINTER_UP}, filling in all the basic values that
+ * define the motion.
+ *
+ * @param displayId the id of the display
+ * @param pointFs location on the screen of the all pointers
+ */
+ public static MotionEvent pointerUpEvent(int displayId, PointF[] pointFs) {
+ final int actionIndex = 1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int action = ACTION_POINTER_UP | actionIndex;
+ return generateMultiplePointersEvent(displayId, action, pointFs);
+ }
+
public static MotionEvent upEvent(int displayId, float x, float y) {
return generateSingleTouchEvent(displayId, ACTION_UP, x, y);
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 4e9ac7c..d4d3128 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -16,6 +16,8 @@
package com.android.server.audio;
+import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -28,13 +30,22 @@
import android.media.AudioSystem;
import android.media.VolumeInfo;
import android.os.test.TestLooper;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
public class AudioDeviceVolumeManagerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final String TAG = "AudioDeviceVolumeManagerTest";
private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
@@ -96,4 +107,91 @@
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
}
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void testConfigurablePreScaleAbsoluteVolume() throws Exception {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
+ final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "fake_ble");
+ final int maxPreScaleIndex = 3;
+ final float[] preScale = new float[3];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final int targetIndex = (int) (preScale[i] * maxIndex);
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mPackageName);
+ mTestLooper.dispatchAll();
+
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ AudioManager.STREAM_MUSIC, targetIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mPackageName);
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ AudioManager.STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void testDisablePreScaleAbsoluteVolume() throws Exception {
+ AudioManager am = mContext.getSystemService(AudioManager.class);
+ final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
+ final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "bla");
+ final int maxPreScaleIndex = 3;
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mPackageName);
+ mTestLooper.dispatchAll();
+
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ AudioManager.STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mPackageName);
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ AudioManager.STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index ad09ef0..061b8ff 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -79,7 +79,7 @@
final AudioDeviceAttributes dev3 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop");
- doNothing().when(mSpyDeviceBroker).persistAudioDeviceSettings();
+ doNothing().when(mSpyDeviceBroker).postPersistAudioDeviceSettings();
mSpatHelper.initForTest(true /*binaural*/, true /*transaural*/);
// test with single device
diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
new file mode 100644
index 0000000..f537efd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
@@ -0,0 +1,552 @@
+/*
+ * 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.pdb;
+
+import static com.android.server.pdb.PersistentDataBlockService.DIGEST_SIZE_BYTES;
+import static com.android.server.pdb.PersistentDataBlockService.MAX_DATA_BLOCK_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.MAX_FRP_CREDENTIAL_HANDLE_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.MAX_TEST_MODE_DATA_SIZE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertThrows;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.service.persistentdata.IPersistentDataBlockService;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
+
+@RunWith(JUnitParamsRunner.class)
+public class PersistentDataBlockServiceTest {
+ private static final String TAG = "PersistentDataBlockServiceTest";
+
+ private static final byte[] SMALL_DATA = "data to write".getBytes();
+ private static final byte[] ANOTHER_SMALL_DATA = "something else".getBytes();
+
+ private Context mContext;
+ private PersistentDataBlockService mPdbService;
+ private IPersistentDataBlockService mInterface;
+ private PersistentDataBlockManagerInternal mInternalInterface;
+ private File mDataBlockFile;
+ private String mOemUnlockPropertyValue;
+
+ @Mock private UserManager mUserManager;
+
+ private class FakePersistentDataBlockService extends PersistentDataBlockService {
+ FakePersistentDataBlockService(Context context, String dataBlockFile,
+ long blockDeviceSize) {
+ super(context, /* isFileBacked */ true, dataBlockFile, blockDeviceSize);
+ }
+
+ @Override
+ void setProperty(String key, String value) {
+ // Override to capture the value instead of actually setting the property.
+ assertThat(key).isEqualTo("sys.oem_unlock_allowed");
+ mOemUnlockPropertyValue = value;
+ }
+ }
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDataBlockFile = mTemporaryFolder.newFile();
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(),
+ /* blockDeviceSize */ -1);
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+ mPdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false);
+ mInterface = mPdbService.getInterfaceForTesting();
+ mInternalInterface = mPdbService.getInternalInterfaceForTesting();
+
+ when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+ }
+
+ abstract static class Block {
+ public PersistentDataBlockService service;
+
+ abstract int write(byte[] data) throws RemoteException;
+ abstract byte[] read() throws RemoteException;
+ }
+
+ /**
+ * Configuration for parameterizing tests, including the block name, maximum block size, and
+ * a block implementation for the read/write operations.
+ */
+ public Object[][] getTestParametersForBlocks() {
+ return new Object[][] {
+ {
+ new Block() {
+ @Override public int write(byte[] data) throws RemoteException {
+ return service.getInterfaceForTesting().write(data);
+ }
+
+ @Override public byte[] read() throws RemoteException {
+ return service.getInterfaceForTesting().read();
+ }
+ },
+ },
+ {
+ new Block() {
+ @Override public int write(byte[] data) {
+ service.getInternalInterfaceForTesting().setFrpCredentialHandle(data);
+ // The written size isn't returned. Pretend it's fully written in the
+ // test for now.
+ return data.length;
+ }
+
+ @Override public byte[] read() {
+ return service.getInternalInterfaceForTesting().getFrpCredentialHandle();
+ }
+ },
+ },
+ {
+ new Block() {
+ @Override public int write(byte[] data) {
+ service.getInternalInterfaceForTesting().setTestHarnessModeData(data);
+ // The written size isn't returned. Pretend it's fully written in the
+ // test for now.
+ return data.length;
+ }
+
+ @Override public byte[] read() {
+ return service.getInternalInterfaceForTesting().getTestHarnessModeData();
+ }
+ },
+ },
+ };
+ }
+
+ @Test
+ @Parameters(method = "getTestParametersForBlocks")
+ public void writeThenRead(Block block) throws Exception {
+ block.service = mPdbService;
+ assertThat(block.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+ assertThat(block.read()).isEqualTo(SMALL_DATA);
+ }
+
+ @Test
+ @Parameters(method = "getTestParametersForBlocks")
+ public void writeWhileAlreadyCorrupted(Block block) throws Exception {
+ block.service = mPdbService;
+ assertThat(block.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+ assertThat(block.read()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ // In the currently implementation, expect the write to not trigger formatting.
+ assertThat(block.write(ANOTHER_SMALL_DATA)).isEqualTo(ANOTHER_SMALL_DATA.length);
+ }
+
+ @Test
+ public void frpWriteOutOfBound() throws Exception {
+ byte[] maxData = new byte[mPdbService.getMaximumFrpDataSize()];
+ assertThat(mInterface.write(maxData)).isEqualTo(maxData.length);
+
+ byte[] overflowData = new byte[mPdbService.getMaximumFrpDataSize() + 1];
+ assertThat(mInterface.write(overflowData)).isLessThan(0);
+ }
+
+ @Test
+ public void frpCredentialWriteOutOfBound() throws Exception {
+ byte[] maxData = new byte[MAX_FRP_CREDENTIAL_HANDLE_SIZE];
+ mInternalInterface.setFrpCredentialHandle(maxData);
+
+ byte[] overflowData = new byte[MAX_FRP_CREDENTIAL_HANDLE_SIZE + 1];
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setFrpCredentialHandle(overflowData));
+ }
+
+ @Test
+ public void testHardnessWriteOutOfBound() throws Exception {
+ byte[] maxData = new byte[MAX_TEST_MODE_DATA_SIZE];
+ mInternalInterface.setTestHarnessModeData(maxData);
+
+ byte[] overflowData = new byte[MAX_TEST_MODE_DATA_SIZE + 1];
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setTestHarnessModeData(overflowData));
+ }
+
+ @Test
+ public void readCorruptedFrpData() throws Exception {
+ assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+ assertThat(mInterface.read()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ // Expect the read to trigger formatting, resulting in reading empty data.
+ assertThat(mInterface.read()).hasLength(0);
+ }
+
+ @Test
+ public void readCorruptedFrpCredentialData() throws Exception {
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(mInternalInterface.getFrpCredentialHandle()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ assertThrows(IllegalStateException.class, () ->
+ mInternalInterface.getFrpCredentialHandle());
+ }
+
+ @Test
+ public void readCorruptedTestHarnessData() throws Exception {
+ mInternalInterface.setTestHarnessModeData(SMALL_DATA);
+ assertThat(mInternalInterface.getTestHarnessModeData()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ assertThrows(IllegalStateException.class, () ->
+ mInternalInterface.getTestHarnessModeData());
+ }
+
+ @Test
+ public void nullWrite() throws Exception {
+ assertThrows(NullPointerException.class, () -> mInterface.write(null));
+ mInternalInterface.setFrpCredentialHandle(null); // no exception
+ mInternalInterface.setTestHarnessModeData(null); // no exception
+ }
+
+ @Test
+ public void emptyDataWrite() throws Exception {
+ var empty = new byte[0];
+ assertThat(mInterface.write(empty)).isEqualTo(0);
+
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setFrpCredentialHandle(empty));
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setTestHarnessModeData(empty));
+ }
+
+ @Test
+ public void frpWriteMoreThan100K() throws Exception {
+ File dataBlockFile = mTemporaryFolder.newFile();
+ PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext,
+ dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000);
+ pdbService.setAllowedUid(Binder.getCallingUid());
+ pdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false);
+
+ IPersistentDataBlockService service = pdbService.getInterfaceForTesting();
+ int maxDataSize = (int) service.getMaximumDataBlockSize();
+ assertThat(service.write(new byte[maxDataSize])).isEqualTo(maxDataSize);
+ assertThat(service.write(new byte[maxDataSize + 1])).isEqualTo(-MAX_DATA_BLOCK_SIZE);
+ }
+
+ @Test
+ public void frpBlockReadWriteWithoutPermission() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ assertThrows(SecurityException.class, () -> mInterface.write(SMALL_DATA));
+ assertThrows(SecurityException.class, () -> mInterface.read());
+ }
+
+ @Test
+ public void getMaximumDataBlockSizeDenied() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ assertThrows(SecurityException.class, () -> mInterface.getMaximumDataBlockSize());
+ }
+
+ @Test
+ public void getMaximumDataBlockSize() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+ assertThat(mInterface.getMaximumDataBlockSize())
+ .isEqualTo(mPdbService.getMaximumFrpDataSize());
+ }
+
+ @Test
+ public void getMaximumDataBlockSizeOfLargerPartition() throws Exception {
+ File dataBlockFile = mTemporaryFolder.newFile();
+ PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext,
+ dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000);
+ pdbService.setAllowedUid(Binder.getCallingUid());
+ pdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false);
+
+ IPersistentDataBlockService service = pdbService.getInterfaceForTesting();
+ assertThat(service.getMaximumDataBlockSize()).isEqualTo(MAX_DATA_BLOCK_SIZE);
+ }
+
+ @Test
+ public void getFrpDataBlockSizeGrantedByUid() throws Exception {
+ assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(SMALL_DATA.length);
+
+ // Modify the magic / type marker. In the current implementation, getting the FRP data block
+ // size does not check digest.
+ tamperWithMagic();
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(0);
+ }
+
+ @Test
+ public void getFrpDataBlockSizeGrantedByPermission() throws Exception {
+ assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ grantAccessPdbStatePermission();
+
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(SMALL_DATA.length);
+
+ // Modify the magic / type marker. In the current implementation, getting the FRP data block
+ // size does not check digest.
+ tamperWithMagic();
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(0);
+ }
+
+ @Test
+ public void wipePermissionCheck() throws Exception {
+ denyOemUnlockPermission();
+ assertThrows(SecurityException.class, () -> mInterface.wipe());
+ }
+
+ @Test
+ public void wipeMakesItNotWritable() throws Exception {
+ grantOemUnlockPermission();
+ mInterface.wipe();
+
+ // Verify that nothing is written.
+ final int headerAndDataBytes = 4 + SMALL_DATA.length;
+ assertThat(mInterface.write(SMALL_DATA)).isLessThan(0);
+ assertThat(readBackingFile(DIGEST_SIZE_BYTES + 4, headerAndDataBytes).array())
+ .isEqualTo(new byte[headerAndDataBytes]);
+
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(readBackingFile(mPdbService.getFrpCredentialDataOffset() + 4,
+ headerAndDataBytes)
+ .array())
+ .isEqualTo(new byte[headerAndDataBytes]);
+
+ mInternalInterface.setTestHarnessModeData(SMALL_DATA);
+ assertThat(readBackingFile(mPdbService.getTestHarnessModeDataOffset() + 4,
+ headerAndDataBytes)
+ .array())
+ .isEqualTo(new byte[headerAndDataBytes]);
+ }
+
+ @Test
+ public void hasFrpCredentialHandleGrantedByUid() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+
+ assertThat(mInterface.hasFrpCredentialHandle()).isFalse();
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(mInterface.hasFrpCredentialHandle()).isTrue();
+ }
+
+ @Test
+ public void hasFrpCredentialHandleGrantedByPermission() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ grantAccessPdbStatePermission();
+
+ assertThat(mInterface.hasFrpCredentialHandle()).isFalse();
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(mInterface.hasFrpCredentialHandle()).isTrue();
+ }
+
+ @Test
+ public void clearTestHarnessModeData() throws Exception {
+ mInternalInterface.setTestHarnessModeData(SMALL_DATA);
+ mInternalInterface.clearTestHarnessModeData();
+
+ assertThat(readBackingFile(mPdbService.getTestHarnessModeDataOffset(),
+ MAX_TEST_MODE_DATA_SIZE).array())
+ .isEqualTo(new byte[MAX_TEST_MODE_DATA_SIZE]);
+ }
+
+ @Test
+ public void getAllowedUid() throws Exception {
+ assertThat(mInternalInterface.getAllowedUid()).isEqualTo(Binder.getCallingUid());
+ }
+
+ @Test
+ public void oemUnlockWithoutPermission() throws Exception {
+ denyOemUnlockPermission();
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlockNotAdmin() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(false);
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlock() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+
+ mInterface.setOemUnlockEnabled(true);
+ assertThat(mInterface.getOemUnlockEnabled()).isTrue();
+ assertThat(mOemUnlockPropertyValue).isEqualTo("1");
+ }
+
+ @Test
+ public void oemUnlockUserRestriction_OemUnlock() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_OEM_UNLOCK)))
+ .thenReturn(true);
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlockUserRestriction_FactoryReset() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_FACTORY_RESET)))
+ .thenReturn(true);
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlockIgnoreTampering() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+
+ // The current implementation does not check digest before set or get the oem unlock bit.
+ tamperWithDigest();
+ mInterface.setOemUnlockEnabled(true);
+ assertThat(mOemUnlockPropertyValue).isEqualTo("1");
+ tamperWithDigest();
+ assertThat(mInterface.getOemUnlockEnabled()).isTrue();
+ }
+
+ @Test
+ public void getOemUnlockEnabledPermissionCheck_NoPermission() throws Exception {
+ assertThrows(SecurityException.class, () -> mInterface.getOemUnlockEnabled());
+ }
+
+ @Test
+ public void getOemUnlockEnabledPermissionCheck_OemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ assertThat(mInterface.getOemUnlockEnabled()).isFalse();
+ }
+
+ @Test
+ public void getOemUnlockEnabledPermissionCheck_ReadOemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.READ_OEM_UNLOCK_STATE));
+ assertThat(mInterface.getOemUnlockEnabled()).isFalse();
+ }
+
+ @Test
+ public void forceOemUnlock_RequiresNoPermission() throws Exception {
+ denyOemUnlockPermission();
+
+ mInternalInterface.forceOemUnlockEnabled(true);
+
+ assertThat(mOemUnlockPropertyValue).isEqualTo("1");
+ assertThat(readBackingFile(mPdbService.getOemUnlockDataOffset(), 1).array())
+ .isEqualTo(new byte[] { 1 });
+ }
+
+ @Test
+ public void getFlashLockStatePermissionCheck_NoPermission() throws Exception {
+ assertThrows(SecurityException.class, () -> mInterface.getFlashLockState());
+ }
+
+ @Test
+ public void getFlashLockStatePermissionCheck_OemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ mInterface.getFlashLockState(); // Do not throw
+ }
+
+ @Test
+ public void getFlashLockStatePermissionCheck_ReadOemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.READ_OEM_UNLOCK_STATE));
+ mInterface.getFlashLockState(); // Do not throw
+ }
+
+ private void tamperWithDigest() throws Exception {
+ try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE)) {
+ ch.write(ByteBuffer.wrap("tampered-digest".getBytes()));
+ }
+ }
+
+ private void tamperWithMagic() throws Exception {
+ try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE)) {
+ ch.write(ByteBuffer.wrap("mark".getBytes()), DIGEST_SIZE_BYTES);
+ }
+ }
+
+ private void makeUserAdmin(boolean isAdmin) {
+ when(mUserManager.isUserAdmin(anyInt())).thenReturn(isAdmin);
+ }
+
+ private void grantOemUnlockPermission() {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ doNothing().when(mContext)
+ .enforceCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE),
+ anyString());
+ }
+
+ private void denyOemUnlockPermission() {
+ doReturn(PackageManager.PERMISSION_DENIED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ }
+
+ private void grantAccessPdbStatePermission() {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingPermission(eq(Manifest.permission.ACCESS_PDB_STATE));
+ }
+
+ private ByteBuffer readBackingFile(long position, int size) throws Exception {
+ try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.READ)) {
+ var buffer = ByteBuffer.allocate(size);
+ assertThat(ch.read(buffer, position)).isGreaterThan(0);
+ return buffer;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index 01a91c1..398148f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -30,7 +30,6 @@
import static org.junit.Assert.fail;
import android.app.AppGlobals;
-import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
@@ -282,16 +281,12 @@
};
iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher);
final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
- AttributionSource attributionSource =
- new AttributionSource.Builder(testPackageUid)
- .setPackageName(TEST_APP_PACKAGE_NAME)
- .build();
- int opMode = iAppOps.checkOperationWithState(code, attributionSource.asState());
+ int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED,
opMode);
suspendTestPackage(null, null, null);
assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
- opMode = iAppOps.checkOperationWithState(code, attributionSource.asState());
+ opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED,
opMode);
iAppOps.stopWatchingMode(watcher);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
deleted file mode 100644
index a0f46e1..0000000
--- a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /services/core/java/com/android/server/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING
deleted file mode 100644
index a83d8bf..0000000
--- a/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timedetector."
- }
- ]
- }
- ]
-}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
deleted file mode 100644
index d64cbcd..0000000
--- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /services/core/java/com/android/server/timezone/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING
deleted file mode 100644
index f59188c..0000000
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
- "postsubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {
- "include-filter": "com.android.server.timezonedetector."
- }
- ]
- }
- ]
-}
diff --git a/services/tests/timetests/Android.bp b/services/tests/timetests/Android.bp
new file mode 100644
index 0000000..23ab859
--- /dev/null
+++ b/services/tests/timetests/Android.bp
@@ -0,0 +1,27 @@
+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"],
+}
+
+android_test {
+ name: "FrameworksTimeServicesTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "device-time-shell-utils",
+ "junit",
+ "junit-params",
+ "mockito-target-minus-junit4",
+ "platform-test-annotations",
+ "services.core",
+ "truth",
+ ],
+ libs: ["android.test.runner"],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/services/tests/timetests/AndroidManifest.xml b/services/tests/timetests/AndroidManifest.xml
new file mode 100644
index 0000000..a21d383
--- /dev/null
+++ b/services/tests/timetests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.framework.services.tests.time">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.framework.services.tests.time"
+ android:label="Frameworks Time Services Tests" />
+
+</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS b/services/tests/timetests/OWNERS
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
rename to services/tests/timetests/OWNERS
diff --git a/services/tests/timetests/TEST_MAPPING b/services/tests/timetests/TEST_MAPPING
new file mode 100644
index 0000000..b24010c
--- /dev/null
+++ b/services/tests/timetests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+ "postsubmit": [
+ {
+ "name": "FrameworksTimeServicesTests"
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java b/services/tests/timetests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java
index 808c1ec..392a48f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java
@@ -33,14 +33,14 @@
import com.android.server.timedetector.TimeDetectorStrategy.Origin;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Instant;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
/**
* Tests for {@link ConfigurationInternal} and associated {@link TimeCapabilitiesAndConfig}
* behavior.
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
rename to services/tests/timetests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/timetests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
rename to services/tests/timetests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/timetests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index dd58135..c64ec72 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -51,6 +51,9 @@
import com.android.server.timezonedetector.StateChangeListener;
import com.android.server.timezonedetector.TestStateChangeListener;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,9 +65,6 @@
import java.util.ArrayList;
import java.util.List;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
@RunWith(JUnitParamsRunner.class)
public class TimeDetectorStrategyImplTest {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 566c6b0..c77fe39 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -35,12 +35,12 @@
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Tests for {@link ConfigurationInternal} and associated {@link TimeZoneCapabilitiesAndConfig}
* behavior.
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 1e72369..7439307 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -43,7 +43,7 @@
this::notifyChangeListeners);
}
- public void initializeConfigurationAndStatus(
+ void initializeConfigurationAndStatus(
ConfigurationInternal configuration, TimeZoneDetectorStatus status) {
mFakeServiceConfigAccessor.initializeCurrentUserConfiguration(configuration);
mStatus = Objects.requireNonNull(status);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java b/services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
similarity index 89%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
index b96c82f..72b1819 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
@@ -29,10 +29,17 @@
public final class ShellCommandTestSupport {
private ShellCommandTestSupport() {}
+ /**
+ * Returns a {@link ShellCommand} from the supplied String, where elements of the command are
+ * separated with spaces. No escaping is performed.
+ */
public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) {
return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" ")));
}
+ /**
+ * Returns a {@link ShellCommand} from the supplied list of command line elements.
+ */
public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) {
ShellCommand command = mock(ShellCommand.class);
class ArgProvider {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
index f45b3a8..56db9cc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
@@ -28,12 +28,18 @@
private int mCallingUserId;
private Integer mCurrentCallingUserId;
+ /** Initializes the calling user ID. */
public void initializeCallingUserId(@UserIdInt int userId) {
mCallingUserId = userId;
mCurrentCallingUserId = userId;
}
@Override
+ public int resolveUserId(int userId, String debugInfo) {
+ return userId;
+ }
+
+ @Override
public int getCallingUserId() {
assertNotNull("callingUserId has been cleared", mCurrentCallingUserId);
return mCurrentCallingUserId;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
index aad06d8..61f9260 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
@@ -23,6 +23,7 @@
private Integer mCurrentUserId;
+ /** Initializes the current user ID. */
public void initializeCurrentUserId(@UserIdInt int userId) {
mCurrentUserId = userId;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestHandler.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestHandler.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestState.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestState.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java
index 9cbf0a3..23f96a2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java
@@ -27,6 +27,7 @@
mNotificationsReceived++;
}
+ /** Asserts the expected number of notifications have been received, then resets the count. */
public void assertNotificationsReceivedAndReset(int expectedCount) {
assertNotificationsReceived(expectedCount);
resetNotificationsReceivedCount();
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 03d406f..e52e8b6 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -72,6 +72,9 @@
import com.android.server.SystemTimeZone.TimeZoneConfidence;
import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,9 +86,6 @@
import java.util.List;
import java.util.function.Function;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
/**
* White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
*/
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
index f8d169b..e05d84f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
@@ -38,6 +38,7 @@
return timeZoneProviderEvent;
}
+ /** Enters a mode where {@link #preProcess} will always return "uncertain" events. */
public void enterUncertainMode() {
mIsUncertain = true;
}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestSupport.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/TestSupport.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 3ba9400..261b5d3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static junit.framework.TestCase.assertEquals;
@@ -24,9 +25,12 @@
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
+import android.app.Flags;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
import android.net.Uri;
+import android.os.Parcel;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
@@ -41,6 +45,7 @@
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.UiServiceTestCase;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParserException;
@@ -55,6 +60,28 @@
@RunWith(AndroidJUnit4.class)
public class ZenModeConfigTest extends UiServiceTestCase {
+ private final String NAME = "name";
+ private final ComponentName OWNER = new ComponentName("pkg", "cls");
+ private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+ private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+ private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+ .authority("authority")
+ .appendPath("path")
+ .appendPath("test")
+ .build();
+
+ private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
+ private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+ private final int TYPE = TYPE_BEDTIME;
+ private final boolean ALLOW_MANUAL = true;
+ private final int ICON_RES_ID = 1234;
+ private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+ private final boolean ENABLED = true;
+ private final int CREATION_TIME = 123;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Test
public void testPriorityOnlyMutingAllNotifications() {
ZenModeConfig config = getMutedRingerConfig();
@@ -202,7 +229,59 @@
}
@Test
- public void testRuleXml() throws Exception {
+ public void testWriteToParcel() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = CONFIG_ACTIVITY;
+ rule.component = OWNER;
+ rule.conditionId = CONDITION_ID;
+ rule.condition = CONDITION;
+ rule.enabled = ENABLED;
+ rule.creationTime = 123;
+ rule.id = "id";
+ rule.zenMode = INTERRUPTION_FILTER;
+ rule.modified = true;
+ rule.name = NAME;
+ rule.snoozing = true;
+ rule.pkg = OWNER.getPackageName();
+ rule.zenPolicy = POLICY;
+
+ rule.allowManualInvocation = ALLOW_MANUAL;
+ rule.type = TYPE;
+ rule.iconResId = ICON_RES_ID;
+ rule.triggerDescription = TRIGGER_DESC;
+
+ Parcel parcel = Parcel.obtain();
+ rule.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ZenModeConfig.ZenRule parceled = new ZenModeConfig.ZenRule(parcel);
+
+ assertEquals(rule.pkg, parceled.pkg);
+ assertEquals(rule.snoozing, parceled.snoozing);
+ assertEquals(rule.enabler, parceled.enabler);
+ assertEquals(rule.component, parceled.component);
+ assertEquals(rule.configurationActivity, parceled.configurationActivity);
+ assertEquals(rule.condition, parceled.condition);
+ assertEquals(rule.enabled, parceled.enabled);
+ assertEquals(rule.creationTime, parceled.creationTime);
+ assertEquals(rule.modified, parceled.modified);
+ assertEquals(rule.conditionId, parceled.conditionId);
+ assertEquals(rule.name, parceled.name);
+ assertEquals(rule.zenMode, parceled.zenMode);
+
+ assertEquals(rule.allowManualInvocation, parceled.allowManualInvocation);
+ assertEquals(rule.iconResId, parceled.iconResId);
+ assertEquals(rule.type, parceled.type);
+ assertEquals(rule.triggerDescription, parceled.triggerDescription);
+ assertEquals(rule.zenPolicy, parceled.zenPolicy);
+ assertEquals(rule, parceled);
+ assertEquals(rule.hashCode(), parceled.hashCode());
+
+ }
+
+ @Test
+ public void testRuleXml_classic() throws Exception {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = new ComponentName("a", "a");
rule.component = new ComponentName("b", "b");
@@ -239,6 +318,58 @@
}
@Test
+ public void testRuleXml() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = CONFIG_ACTIVITY;
+ rule.component = OWNER;
+ rule.conditionId = CONDITION_ID;
+ rule.condition = CONDITION;
+ rule.enabled = ENABLED;
+ rule.creationTime = 123;
+ rule.id = "id";
+ rule.zenMode = INTERRUPTION_FILTER;
+ rule.modified = true;
+ rule.name = NAME;
+ rule.snoozing = true;
+ rule.pkg = OWNER.getPackageName();
+ rule.zenPolicy = POLICY;
+ rule.creationTime = CREATION_TIME;
+
+ rule.allowManualInvocation = ALLOW_MANUAL;
+ rule.type = TYPE;
+ rule.iconResId = ICON_RES_ID;
+ rule.triggerDescription = TRIGGER_DESC;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeRuleXml(rule, baos);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+ assertEquals(rule.pkg, fromXml.pkg);
+ // always resets on reboot
+ assertFalse(fromXml.snoozing);
+ //should all match original
+ assertEquals(rule.component, fromXml.component);
+ assertEquals(rule.configurationActivity, fromXml.configurationActivity);
+ assertNull(fromXml.enabler);
+ assertEquals(rule.condition, fromXml.condition);
+ assertEquals(rule.enabled, fromXml.enabled);
+ assertEquals(rule.creationTime, fromXml.creationTime);
+ assertEquals(rule.modified, fromXml.modified);
+ assertEquals(rule.conditionId, fromXml.conditionId);
+ assertEquals(rule.name, fromXml.name);
+ assertEquals(rule.zenMode, fromXml.zenMode);
+ assertEquals(rule.creationTime, fromXml.creationTime);
+
+ assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation);
+ assertEquals(rule.type, fromXml.type);
+ assertEquals(rule.triggerDescription, fromXml.triggerDescription);
+ assertEquals(rule.iconResId, fromXml.iconResId);
+ }
+
+ @Test
public void testRuleXml_pkg_component() throws Exception {
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = new ComponentName("a", "a");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index bcd807a..fd3d5e9b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -23,6 +23,7 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import android.app.AutomaticZenRule;
import android.content.ComponentName;
import android.net.Uri;
import android.provider.Settings;
@@ -229,6 +230,10 @@
rule.name = "name";
rule.snoozing = true;
rule.pkg = "a";
+ rule.allowManualInvocation = true;
+ rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
+ rule.iconResId = 123;
+ rule.triggerDescription = "At night";
return rule;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index e22c104..0349ad9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.AutomaticZenRule.TYPE_BEDTIME;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
@@ -71,6 +72,7 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
+import android.app.Flags;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.content.ComponentName;
@@ -88,6 +90,7 @@
import android.net.Uri;
import android.os.Process;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
@@ -120,6 +123,7 @@
import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -150,6 +154,28 @@
private static final int CUSTOM_PKG_UID = 1;
private static final String CUSTOM_RULE_ID = "custom_rule";
+ private final String NAME = "name";
+ private final ComponentName OWNER = new ComponentName("pkg", "cls");
+ private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+ private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+ private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+ .authority("authority")
+ .appendPath("path")
+ .appendPath("test")
+ .build();
+
+ private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
+ private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+ private final int TYPE = TYPE_BEDTIME;
+ private final boolean ALLOW_MANUAL = true;
+ private final int ICON_RES_ID = 1234;
+ private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+ private final boolean ENABLED = true;
+ private final int CREATION_TIME = 123;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
ConditionProviders mConditionProviders;
@Mock NotificationManager mNotificationManager;
@Mock PackageManager mPackageManager;
@@ -1961,6 +1987,26 @@
@Test
public void testSetManualZenMode() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ setupZenConfig();
+
+ // note that caller=null because that's how it comes in from NMS.setZenMode
+ mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
+ Process.SYSTEM_UID, true);
+
+ // confirm that setting zen mode via setManualZenMode changed the zen mode correctly
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
+ assertEquals(true, mZenModeHelper.mConfig.manualRule.allowManualInvocation);
+
+ // and also that it works to turn it back off again
+ mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+ Process.SYSTEM_UID, true);
+
+ assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+ }
+
+ @Test
+ public void testSetManualZenMode_legacy() {
setupZenConfig();
// note that caller=null because that's how it comes in from NMS.setZenMode
@@ -2607,6 +2653,47 @@
assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking()); // custom stricter
}
+ @Test
+ public void testCreateAutomaticZenRule_allFields() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = CONFIG_ACTIVITY;
+ rule.component = OWNER;
+ rule.conditionId = CONDITION_ID;
+ rule.condition = CONDITION;
+ rule.enabled = ENABLED;
+ rule.creationTime = 123;
+ rule.id = "id";
+ rule.zenMode = INTERRUPTION_FILTER;
+ rule.modified = true;
+ rule.name = NAME;
+ rule.snoozing = true;
+ rule.pkg = OWNER.getPackageName();
+ rule.zenPolicy = POLICY;
+
+ rule.allowManualInvocation = ALLOW_MANUAL;
+ rule.type = TYPE;
+ rule.iconResId = ICON_RES_ID;
+ rule.triggerDescription = TRIGGER_DESC;
+
+ AutomaticZenRule actual = mZenModeHelper.createAutomaticZenRule(rule);
+
+ assertEquals(NAME, actual.getName());
+ assertEquals(OWNER, actual.getOwner());
+ assertEquals(CONDITION_ID, actual.getConditionId());
+ assertEquals(NotificationManager.INTERRUPTION_FILTER_ALARMS,
+ actual.getInterruptionFilter());
+ assertEquals(ENABLED, actual.isEnabled());
+ assertEquals(POLICY, actual.getZenPolicy());
+ assertEquals(CONFIG_ACTIVITY, actual.getConfigurationActivity());
+ assertEquals(TYPE, actual.getType());
+ assertEquals(ALLOW_MANUAL, actual.isManualInvocationAllowed());
+ assertEquals(CREATION_TIME, actual.getCreationTime());
+ assertEquals(OWNER.getPackageName(), actual.getPackageName());
+ assertEquals(ICON_RES_ID, actual.getIconResId());
+ assertEquals(TRIGGER_DESC, actual.getTriggerDescription());
+ }
+
private void setupZenConfig() {
mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
mZenModeHelper.mConfig.allowAlarms = false;
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 bf86563..c782d3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -384,33 +384,36 @@
assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
assertFalse(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
- navbar.removeIfPossible();
- assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
- di.logicalHeight).mNonDecorInsets.bottom);
-
final WindowState statusBar = createStatusBarWithProvidedInsets(mDisplayContent);
- assertTrue(statusBar.providesDisplayDecorInsets()
- && displayPolicy.updateDecorInsetsInfo());
- assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
- di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+ if (mWm.mConfigTypes == WindowInsets.Type.navigationBars()) {
+ assertFalse(statusBar.providesDisplayDecorInsets()
+ && displayPolicy.updateDecorInsetsInfo());
+ assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation,
+ di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+ } else {
+ assertTrue(statusBar.providesDisplayDecorInsets()
+ && displayPolicy.updateDecorInsetsInfo());
+ assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
+ di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+ }
// Add a window that provides the same insets in current rotation. But it specifies
// different insets in other rotations.
- final WindowState bar2 = createWindow(null, statusBar.mAttrs.type, "bar2");
+ final WindowState bar2 = createWindow(null, navbar.mAttrs.type, "bar2");
bar2.mAttrs.providedInsets = new InsetsFrameProvider[] {
- new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
- .setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
+ new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
+ .setInsetsSize(Insets.of(0, 0, 0, NAV_BAR_HEIGHT))
};
bar2.mAttrs.setFitInsetsTypes(0);
bar2.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
- final int doubleHeightFor90 = STATUS_BAR_HEIGHT * 2;
+ final int doubleHeightFor90 = NAV_BAR_HEIGHT * 2;
for (int i = ROTATION_0; i <= Surface.ROTATION_270; i++) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.setFitInsetsTypes(0);
if (i == Surface.ROTATION_90) {
params.providedInsets = new InsetsFrameProvider[] {
- new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
- .setInsetsSize(Insets.of(0, doubleHeightFor90, 0, 0))
+ new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
+ .setInsetsSize(Insets.of(0, 0, 0, doubleHeightFor90))
};
} else {
params.providedInsets = bar2.mAttrs.providedInsets;
@@ -422,7 +425,12 @@
assertFalse(displayPolicy.updateDecorInsetsInfo());
// The insets in other rotations should be still updated.
assertEquals(doubleHeightFor90, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90,
- di.logicalHeight, di.logicalWidth).mConfigInsets.top);
+ di.logicalHeight, di.logicalWidth).mConfigInsets.bottom);
+
+ navbar.removeIfPossible();
+ bar2.removeIfPossible();
+ assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
+ di.logicalHeight).mNonDecorInsets.bottom);
}
@SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index c4d03be..49a8886 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -282,6 +283,9 @@
assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ if (explicitRefreshRateHints()) {
+ return;
+ }
overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
@@ -320,6 +324,9 @@
assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
+ if (explicitRefreshRateHints()) {
+ return;
+ }
overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
@@ -342,6 +349,9 @@
assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE);
assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
+ if (explicitRefreshRateHints()) {
+ return;
+ }
window.mActivityRecord.mSurfaceAnimator.startAnimation(
window.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 03188f8..9af5ba5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -376,6 +376,9 @@
mWmService.onInitReady();
mAtmService.setWindowManager(mWmService);
+ // Avoid real display events interfering with the test. Also avoid leaking registration.
+ mContext.getSystemService(DisplayManager.class)
+ .unregisterDisplayListener(mAtmService.mRootWindowContainer);
mWmService.mDisplayEnabled = true;
mWmService.mDisplayReady = true;
mAtmService.getTransitionController().mIsWaitingForDisplayEnabled = false;
@@ -413,12 +416,6 @@
}
}
- if (mAtmService != null) {
- // Unregister display listener from root to avoid issues with subsequent tests.
- mContext.getSystemService(DisplayManager.class)
- .unregisterDisplayListener(mAtmService.mRootWindowContainer);
- }
-
for (int i = mDeviceConfigListeners.size() - 1; i >= 0; i--) {
DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListeners.get(i));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 7822071..ec068be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -27,12 +27,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -130,17 +126,6 @@
}
@Test
- public void testUpdateOrganizedTaskFragmentSurface_noSurfaceUpdateWhenOrganizedBySystem() {
- clearInvocations(mTransaction);
- mTaskFragment.mIsSurfaceManagedBySystemOrganizer = true;
-
- mTaskFragment.updateOrganizedTaskFragmentSurface();
-
- verify(mTransaction, never()).setPosition(eq(mLeash), anyFloat(), anyFloat());
- verify(mTransaction, never()).setWindowCrop(eq(mLeash), anyInt(), anyInt());
- }
-
- @Test
public void testShouldStartChangeTransition_relativePositionChange() {
final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 8fecbb9..4b54e44 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -132,6 +132,10 @@
mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
mPersister.persistSnapshot(3, mTestUserId, createSnapshot());
mPersister.persistSnapshot(4, mTestUserId, createSnapshot());
+ // Verify there should only keep the latest request when received a duplicated id.
+ mPersister.persistSnapshot(4, mTestUserId, createSnapshot());
+ // Expected 3: One remove obsolete request, two persist request.
+ assertEquals(3, mSnapshotPersistQueue.peekQueueSize());
mSnapshotPersistQueue.setPaused(false);
mSnapshotPersistQueue.waitForQueueEmpty();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2689e9d..a83caa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2454,6 +2454,7 @@
spyOn(perfHinter);
doAnswer(invocation -> {
session[0] = (SystemPerformanceHinter.HighPerfSession) invocation.callRealMethod();
+ spyOn(session[0]);
return session[0];
}).when(perfHinter).createSession(anyInt(), anyInt(), anyString());
}
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 9f3b52e..1dc5dcf 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -33,6 +33,6 @@
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
"android.hardware.usb-V1.3-java",
- "android.hardware.usb-V2-java",
+ "android.hardware.usb-V3-java",
],
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 35e2fcf..fb13b33 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -1285,12 +1285,17 @@
pw.println(" dumpsys usb add-port \"matrix\" dual --compliance-warnings");
pw.println(" dumpsys usb set-compliance-reasons \"matrix\" <reason-list>");
pw.println(" dumpsys usb clear-compliance-reasons \"matrix\"");
- pw.println("<reason-list> is expected to be formatted as \"1, ..., 4\"");
+ pw.println("<reason-list> is expected to be formatted as \"1, ..., N\"");
pw.println("with reasons that need to be simulated.");
pw.println(" 1: other");
pw.println(" 2: debug accessory");
pw.println(" 3: bc12");
pw.println(" 4: missing rp");
+ pw.println(" 5: input power limited");
+ pw.println(" 6: missing data lines");
+ pw.println(" 7: enumeration fail");
+ pw.println(" 8: flaky connection");
+ pw.println(" 9: unreliable io");
pw.println();
pw.println("Example simulate DisplayPort Alt Mode Changes:");
pw.println(" dumpsys usb add-port \"matrix\" dual --displayport");
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
index c7a7a9b..45b623b 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -39,6 +39,7 @@
import android.hardware.usb.AltModeData;
import android.hardware.usb.AltModeData.DisplayPortAltModeData;
import android.hardware.usb.DisplayPortAltModePinAssignment;
+import android.hardware.usb.flags.Flags;
import android.os.Build;
import android.os.ServiceManager;
import android.os.IBinder;
@@ -593,11 +594,21 @@
for (int warning : complianceWarnings) {
if (newComplianceWarnings.indexOf(warning) == -1
&& warning >= UsbPortStatus.COMPLIANCE_WARNING_OTHER) {
- // ComplianceWarnings range from [1, 4] in Android U
- if (warning > UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP) {
- newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ if (Flags.enableUsbDataComplianceWarning()) {
+ // ComplianceWarnings range extends to [1, 9] when feature flag is on
+ if (warning
+ > UsbPortStatus.COMPLIANCE_WARNING_UNRELIABLE_IO) {
+ newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ } else {
+ newComplianceWarnings.add(warning);
+ }
} else {
- newComplianceWarnings.add(warning);
+ // ComplianceWarnings range from [1, 4] in Android U
+ if (warning > UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP) {
+ newComplianceWarnings.add(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ } else {
+ newComplianceWarnings.add(warning);
+ }
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 216f45a..d722f2f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -279,6 +279,7 @@
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
| Context.BIND_SCHEDULE_LIKE_TOP_APP
+ | Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
new UserHandle(mUser));
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index cb7926c..4b1a726 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1308,7 +1308,7 @@
&& mSkip464Xlat == other.mSkip464Xlat
&& mAlwaysOn == other.mAlwaysOn
&& mInfrastructureBitmask == other.mInfrastructureBitmask
- && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning);
+ && mEsimBootstrapProvisioning == other.mEsimBootstrapProvisioning;
}
/**
@@ -1473,7 +1473,7 @@
apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat);
apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);
apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask);
- apnValue.put(Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning);
+ apnValue.put(Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning);
return apnValue;
}
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f61cce6..daaab33 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -156,6 +156,7 @@
method @Deprecated public int getInt(int);
method @Deprecated public long getLong(int);
method @Deprecated public android.net.Uri getNotificationUri();
+ method @Deprecated public java.util.List<android.net.Uri> getNotificationUris();
method @Deprecated public int getPosition();
method @Deprecated public short getShort(int);
method @Deprecated public String getString(int);
@@ -179,6 +180,7 @@
method @Deprecated public android.os.Bundle respond(android.os.Bundle);
method @Deprecated public void setExtras(android.os.Bundle);
method @Deprecated public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method @Deprecated public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>);
method @Deprecated public void unregisterContentObserver(android.database.ContentObserver);
method @Deprecated public void unregisterDataSetObserver(android.database.DataSetObserver);
}
diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp
new file mode 100644
index 0000000..9eeec7c
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+android_test {
+ name: "FlickerTestsOther",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.server.wm.flicker",
+ instrumentation_target_package: "com.android.server.wm.flicker",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
similarity index 91%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
index 6bc7cbe..f867ffb 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flick">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index ed71531..439cf13 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -84,20 +84,6 @@
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml
similarity index 63%
rename from tests/FlickerTests/manifests/AndroidManifestOther.xml
rename to tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
similarity index 99%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
index adff579..955e801 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
@@ -23,9 +23,9 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
similarity index 68%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index 47d6d23..12a57d5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -36,9 +37,8 @@
/**
* Test launching a secondary Activity into Picture-In-Picture mode.
*
- * Setup: Start from a split A|B.
- * Transition: B enters PIP, observe the window first goes fullscreen then shrink to the bottom
- * right corner on screen.
+ * Setup: Start from a split A|B. Transition: B enters PIP, observe the window first goes fullscreen
+ * then shrink to the bottom right corner on screen.
*
* To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
*/
@@ -64,16 +64,10 @@
}
}
- /**
- * We expect the background layer to be visible during this transition.
- */
- @Presubmit
- @Test
- override fun backgroundLayerNeverVisible(): Unit {}
+ /** We expect the background layer to be visible during this transition. */
+ @Presubmit @Test override fun backgroundLayerNeverVisible() {}
- /**
- * Main and secondary activity start from a split each taking half of the screen.
- */
+ /** Main and secondary activity start from a split each taking half of the screen. */
@Presubmit
@Test
fun layersStartFromEqualSplit() {
@@ -156,8 +150,12 @@
ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
}
pipLayerList.zipWithNext { previous, current ->
- // TODO(b/290987990): Add checks for visibleRegion.
- current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+ if (startDisplayBounds.width > startDisplayBounds.height) {
+ // Only verify when the display is landscape, because otherwise the final pip
+ // window can be to the left of the original secondary activity.
+ current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+ }
+ current.screenBounds.overlaps(previous.screenBounds.region)
current.screenBounds.notBiggerThan(previous.screenBounds.region)
}
}
@@ -168,6 +166,61 @@
}
}
+ /** The secondary layer should never jump to the left. */
+ @Presubmit
+ @Test
+ fun secondaryLayerNotJumpToLeft() {
+ flicker.assertLayers {
+ invoke("secondaryLayerNotJumpToLeft") {
+ val secondaryVisibleRegion =
+ it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ if (secondaryVisibleRegion.region.isNotEmpty) {
+ check { "left" }
+ .that(secondaryVisibleRegion.region.bounds.left)
+ .isGreater(0)
+ }
+ }
+ }
+ }
+
+ /**
+ * The pip overlay layer should cover exactly the secondary activity layer when both are
+ * visible.
+ */
+ @Presubmit
+ @Test
+ fun pipContentOverlayLayerCoversExactlySecondaryLayer() {
+ flicker.assertLayers {
+ isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ .then()
+ .isVisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .invoke("pipContentOverlayLayerCoversExactlySecondaryLayer") {
+ val overlayVisibleRegion =
+ it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ val secondaryVisibleRegion =
+ it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region)
+ }
+ .then()
+ .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // Expected for the main activity to become invisible for 1-2 frames because the snapshot
+ // covers it.
+ flicker.assertLayers {
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ )
+ }
+ }
+
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
index 4f7d8a4..e8389d19 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -23,9 +23,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import com.android.server.wm.flicker.rotation.RotationTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
new file mode 100644
index 0000000..1123c5b
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding.rotation
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.setRotation
+import org.junit.Test
+
+/** Base class for app rotation tests */
+abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ protected abstract val testApp: StandardAppHelper
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { this.setRotation(flicker.scenario.startRotation) }
+ teardown { testApp.exit(wmHelper) }
+ transitions { this.setRotation(flicker.scenario.endRotation) }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers =
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher("", "SecondaryHomeHandle")
+ )
+ )
+ }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen at the start of the transition */
+ @Presubmit
+ @Test
+ open fun appLayerRotates_StartingPos() {
+ flicker.assertLayersStart {
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen at the end of the transition */
+ @Presubmit
+ @Test
+ open fun appLayerRotates_EndingPos() {
+ flicker.assertLayersEnd {
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+
+ override fun cujCompleted() {
+ super.cujCompleted()
+ appLayerRotates_StartingPos()
+ appLayerRotates_EndingPos()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
similarity index 63%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
rename to tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 93a5bf5..576eec8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,8 +24,8 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.toFlickerComponent
-import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.utils.SplitScreenUtils
@@ -39,11 +39,11 @@
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/***
+/**
* Test entering System SplitScreen with Activity Embedding Split and another app.
*
- * Setup: Launch A|B in split and secondaryApp, return to home.
- * Transitions: Let AE Split A|B enter splitscreen with secondaryApp. Resulting in A|B|secondaryApp.
+ * Setup: Launch A|B in split and secondaryApp, return to home. Transitions: Let AE Split A|B enter
+ * splitscreen with secondaryApp. Resulting in A|B|secondaryApp.
*
* To run this test: `atest FlickerTestsOther:EnterSystemSplitTest`
*/
@@ -51,8 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
- ActivityEmbeddingTestBase(flicker) {
+class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) {
private val secondaryApp = SplitScreenUtils.getPrimary(instrumentation)
override val transition: FlickerBuilder.() -> Unit = {
@@ -62,17 +61,22 @@
secondaryApp.launchViaIntent(wmHelper)
tapl.goHome()
wmHelper
- .StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds
- ?: error("Display not found")
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
}
transitions {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp,
- flicker.scenario.startRotation)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ testApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp)
}
}
@@ -85,7 +89,10 @@
@Test
fun activityEmbeddingSplitLayerBecomesVisible() {
flicker.splitAppLayerBoundsIsVisibleAtEnd(
- testApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
+ testApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
}
@Presubmit
@@ -96,7 +103,10 @@
@Test
fun secondaryLayerBecomesVisible() {
flicker.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
}
@Presubmit
@@ -105,73 +115,73 @@
/**
* After the transition there should be both ActivityEmbedding activities,
- * SplitScreenPrimaryActivity and the system split divider on screen.
- * Verify the layers are in expected sizes.
+ * SplitScreenPrimaryActivity and the system split divider on screen. Verify the layers are in
+ * expected sizes.
*/
@Presubmit
@Test
fun activityEmbeddingSplitSurfaceAreEven() {
flicker.assertLayersEnd {
val leftAELayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
val rightAELayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
val secondaryAppLayerRegion =
- visibleRegion(
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
+ visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
val systemDivider = visibleRegion(SPLIT_SCREEN_DIVIDER_COMPONENT)
leftAELayerRegion
- .plus(rightAELayerRegion.region)
- .plus(secondaryAppLayerRegion.region)
- .plus(systemDivider.region)
- .coversExactly(startDisplayBounds)
+ .plus(rightAELayerRegion.region)
+ .plus(secondaryAppLayerRegion.region)
+ .plus(systemDivider.region)
+ .coversExactly(startDisplayBounds)
check { "ActivityEmbeddingSplitHeight" }
- .that(leftAELayerRegion.region.height)
- .isEqual(rightAELayerRegion.region.height)
+ .that(leftAELayerRegion.region.height)
+ .isEqual(rightAELayerRegion.region.height)
check { "SystemSplitHeight" }
- .that(rightAELayerRegion.region.height)
- .isEqual(secondaryAppLayerRegion.region.height)
+ .that(rightAELayerRegion.region.height)
+ .isEqual(secondaryAppLayerRegion.region.height)
// TODO(b/292283182): Remove this special case handling.
check { "ActivityEmbeddingSplitWidth" }
- .that(Math.abs(
- leftAELayerRegion.region.width - rightAELayerRegion.region.width))
- .isLower(2)
+ .that(Math.abs(leftAELayerRegion.region.width - rightAELayerRegion.region.width))
+ .isLower(2)
check { "SystemSplitWidth" }
- .that(Math.abs(secondaryAppLayerRegion.region.width -
- 2 * rightAELayerRegion.region.width))
- .isLower(2)
+ .that(
+ Math.abs(
+ secondaryAppLayerRegion.region.width - 2 * rightAELayerRegion.region.width
+ )
+ )
+ .isLower(2)
}
}
- /**
- * Verify the windows are in expected sizes.
- */
+ /** Verify the windows are in expected sizes. */
@Presubmit
@Test
fun activityEmbeddingSplitWindowsAreEven() {
flicker.assertWmEnd {
val leftAEWindowRegion =
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
val rightAEWindowRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
// There's no window for the divider bar.
val secondaryAppLayerRegion =
- visibleRegion(
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
+ visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
check { "ActivityEmbeddingSplitHeight" }
- .that(leftAEWindowRegion.region.height)
- .isEqual(rightAEWindowRegion.region.height)
+ .that(leftAEWindowRegion.region.height)
+ .isEqual(rightAEWindowRegion.region.height)
check { "SystemSplitHeight" }
- .that(rightAEWindowRegion.region.height)
- .isEqual(secondaryAppLayerRegion.region.height)
+ .that(rightAEWindowRegion.region.height)
+ .isEqual(secondaryAppLayerRegion.region.height)
check { "ActivityEmbeddingSplitWidth" }
- .that(Math.abs(
- leftAEWindowRegion.region.width - rightAEWindowRegion.region.width))
- .isLower(2)
+ .that(Math.abs(leftAEWindowRegion.region.width - rightAEWindowRegion.region.width))
+ .isLower(2)
check { "SystemSplitWidth" }
- .that(Math.abs(secondaryAppLayerRegion.region.width -
- 2 * rightAEWindowRegion.region.width))
- .isLower(2)
+ .that(
+ Math.abs(
+ secondaryAppLayerRegion.region.width - 2 * rightAEWindowRegion.region.width
+ )
+ )
+ .isLower(2)
}
}
@@ -191,4 +201,4 @@
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 3d49d81..514f895 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -24,75 +24,14 @@
}
filegroup {
- name: "FlickerTestsBase-src",
- srcs: ["src/com/android/server/wm/flicker/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsAppClose-src",
- srcs: ["src/**/close/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsActivityEmbedding-src",
- srcs: [
- "src/**/activityembedding/*.kt",
- "src/**/activityembedding/open/*.kt",
- "src/**/activityembedding/close/*.kt",
- "src/**/activityembedding/layoutchange/*.kt",
- "src/**/activityembedding/pip/*.kt",
- "src/**/activityembedding/rotation/*.kt",
- "src/**/activityembedding/rtl/*.kt",
- "src/**/activityembedding/splitscreen/*.kt",
- ],
-}
-
-filegroup {
- name: "FlickerTestsIme-src",
- srcs: ["src/**/ime/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsAppLaunchCommon-src",
- srcs: ["src/**/launch/common/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsAppLaunch1-src",
- srcs: ["src/**/launch/OpenApp*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsAppLaunch2-src",
- srcs: ["src/**/launch/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsNotification-src",
- srcs: ["src/**/notification/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsQuickswitch-src",
- srcs: ["src/**/quickswitch/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsRotation-src",
- srcs: ["src/**/rotation/*.kt"],
-}
-
-filegroup {
name: "FlickerServiceTests-src",
srcs: [
- "src/com/android/server/wm/flicker/service/**/*.kt",
+ "src/**/*",
],
}
java_defaults {
name: "FlickerTestsDefault",
- manifest: "manifests/AndroidManifest.xml",
- test_config_template: "AndroidTestTemplate.xml",
platform_apis: true,
certificate: "platform",
optimize: {
@@ -116,139 +55,6 @@
],
}
-android_test {
- name: "FlickerTestsOther",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestOther.xml"],
- package_name: "com.android.server.wm.flicker",
- instrumentation_target_package: "com.android.server.wm.flicker",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- exclude_srcs: [
- ":FlickerTestsAppClose-src",
- ":FlickerTestsIme-src",
- ":FlickerTestsAppLaunch1-src",
- ":FlickerTestsAppLaunch2-src",
- ":FlickerTestsQuickswitch-src",
- ":FlickerTestsRotation-src",
- ":FlickerTestsNotification-src",
- ":FlickerServiceTests-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsAppClose",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestAppClose.xml"],
- package_name: "com.android.server.wm.flicker.close",
- instrumentation_target_package: "com.android.server.wm.flicker.close",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsAppClose-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsIme",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestIme.xml"],
- package_name: "com.android.server.wm.flicker.ime",
- instrumentation_target_package: "com.android.server.wm.flicker.ime",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsIme-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsAppLaunch1",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
- package_name: "com.android.server.wm.flicker.launch",
- instrumentation_target_package: "com.android.server.wm.flicker.launch",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsAppLaunchCommon-src",
- ":FlickerTestsAppLaunch1-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsAppLaunch2",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
- package_name: "com.android.server.wm.flicker.launch",
- instrumentation_target_package: "com.android.server.wm.flicker.launch",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsAppLaunchCommon-src",
- ":FlickerTestsAppLaunch2-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ":FlickerTestsAppLaunch1-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsNotification",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestNotification.xml"],
- package_name: "com.android.server.wm.flicker.notification",
- instrumentation_target_package: "com.android.server.wm.flicker.notification",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsNotification-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsQuickswitch",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestQuickswitch.xml"],
- package_name: "com.android.server.wm.flicker.quickswitch",
- instrumentation_target_package: "com.android.server.wm.flicker.quickswitch",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsQuickswitch-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsRotation",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestRotation.xml"],
- package_name: "com.android.server.wm.flicker.rotation",
- instrumentation_target_package: "com.android.server.wm.flicker.rotation",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsRotation-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
- name: "FlickerServiceTests",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestService.xml"],
- package_name: "com.android.server.wm.flicker.service",
- instrumentation_target_package: "com.android.server.wm.flicker.service",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerServiceTests-src",
- ],
-}
-
java_library {
name: "wm-flicker-common-assertions",
platform_apis: true,
@@ -259,9 +65,6 @@
"src/**/*Assertions.java",
"src/**/*Assertions.kt",
],
- exclude_srcs: [
- "**/helpers/*",
- ],
static_libs: [
"flickerlib",
"flickerlib-helpers",
@@ -270,26 +73,6 @@
],
}
-java_library {
- name: "wm-flicker-common-app-helpers",
- platform_apis: true,
- optimize: {
- enabled: false,
- },
- srcs: [
- "**/helpers/*",
- ],
- static_libs: [
- "flickertestapplib",
- "flickerlib",
- "flickerlib-apphelpers",
- "flickerlib-helpers",
- "truth",
- "app-helpers-core",
- "wm-flicker-window-extensions",
- ],
-}
-
android_library_import {
name: "wm-flicker-window-extensions_nodeps",
aars: ["libs/window-extensions-release.aar"],
@@ -304,3 +87,9 @@
],
installable: false,
}
+
+java_library {
+ name: "FlickerTestsBase",
+ defaults: ["FlickerTestsDefault"],
+ srcs: ["src/**/*"],
+}
diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp
new file mode 100644
index 0000000..151d12f
--- /dev/null
+++ b/tests/FlickerTests/AppClose/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+android_test {
+ name: "FlickerTestsAppClose",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/AppClose/AndroidManifest.xml
similarity index 91%
rename from tests/FlickerTests/manifests/AndroidManifest.xml
rename to tests/FlickerTests/AppClose/AndroidManifest.xml
index 6bc7cbe..e75e178 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/AppClose/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.close">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.close"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index ed71531..4b6224e 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -83,21 +83,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/AppClose/OWNERS
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
rename to tests/FlickerTests/AppClose/OWNERS
diff --git a/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/AppClose/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/AppClose/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/AppClose/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
rename to tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 288558ae..64dd44d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
rename to tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 32305c6..eb256b5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
rename to tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8d752cc..ea025c7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/AppClose/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/AppClose/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp
new file mode 100644
index 0000000..f33384d
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/Android.bp
@@ -0,0 +1,69 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+filegroup {
+ name: "FlickerTestsAppLaunchCommon-src",
+ srcs: ["src/**/common/*"],
+}
+
+filegroup {
+ name: "FlickerTestsAppLaunch1-src",
+ srcs: ["src/**/OpenApp*"],
+}
+
+java_library {
+ name: "FlickerTestsAppLaunchCommon",
+ defaults: ["FlickerTestsDefault"],
+ srcs: [":FlickerTestsAppLaunchCommon-src"],
+ static_libs: ["FlickerTestsBase"],
+}
+
+android_test {
+ name: "FlickerTestsAppLaunch1",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: [":FlickerTestsAppLaunch1-src"],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsAppLaunchCommon",
+ ],
+}
+
+android_test {
+ name: "FlickerTestsAppLaunch2",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ exclude_srcs: [
+ ":FlickerTestsAppLaunchCommon-src",
+ ":FlickerTestsAppLaunch1-src",
+ ],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsAppLaunchCommon",
+ ],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/AppLaunch/AndroidManifest.xml
similarity index 91%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/AppLaunch/AndroidManifest.xml
index 6bc7cbe..b89af1a 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.launch">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.launch"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index ed71531..583bcb7 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -83,21 +83,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/AppLaunch/OWNERS
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
rename to tests/FlickerTests/AppLaunch/OWNERS
diff --git a/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index f788efa..413767c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index d86dc50..4168bdc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index be07053..9c55c98 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index f66eff9..fc6cdb1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
similarity index 99%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 65214764..de666dd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 4d31c28..f8a9961 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index 42e34b3..0aceb35 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
similarity index 97%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
index 97ba99e..f41a2a2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch.common
+package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
@@ -24,6 +24,7 @@
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper
+import com.android.server.wm.flicker.launch.common.OpenAppFromIconTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index 98e3646..93ca41c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -31,7 +31,6 @@
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.R
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
similarity index 99%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index b82a129..9c2899ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
similarity index 99%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
index c854701..802c755 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
@@ -44,4 +44,4 @@
}
teardown { testApp.exit(wmHelper) }
}
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
rename to tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/FlickerService/Android.bp b/tests/FlickerTests/FlickerService/Android.bp
new file mode 100644
index 0000000..1a38115
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+android_test {
+ name: "FlickerServiceTests",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/FlickerService/AndroidManifest.xml
similarity index 91%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/FlickerService/AndroidManifest.xml
index 6bc7cbe..f31e820 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/FlickerService/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.service">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.service"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index ed71531..d6ae2b3 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -83,20 +83,6 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
diff --git a/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/FlickerService/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/FlickerService/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/FlickerService/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
similarity index 99%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
index 3cae1c4..fcf442a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
@@ -19,6 +19,7 @@
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -30,7 +31,6 @@
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
@Ignore("Base Test Class")
abstract class QuickSwitchBetweenTwoAppsForward(val rotation: Rotation = Rotation.ROTATION_0) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt
rename to tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/FlickerService/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
new file mode 100644
index 0000000..057d9fc
--- /dev/null
+++ b/tests/FlickerTests/IME/Android.bp
@@ -0,0 +1,78 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+filegroup {
+ name: "FlickerTestsImeCommon-src",
+ srcs: ["src/**/common/*"],
+}
+
+filegroup {
+ name: "FlickerTestsIme1-src",
+ srcs: ["src/**/Close*"],
+}
+
+android_test {
+ name: "FlickerTestsIme",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
+
+java_library {
+ name: "FlickerTestsImeCommon",
+ defaults: ["FlickerTestsDefault"],
+ srcs: [":FlickerTestsImeCommon-src"],
+ static_libs: ["FlickerTestsBase"],
+}
+
+android_test {
+ name: "FlickerTestsIme1",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: [":FlickerTestsIme1-src"],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsImeCommon",
+ ],
+}
+
+android_test {
+ name: "FlickerTestsIme2",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ exclude_srcs: [
+ ":FlickerTestsIme1-src",
+ ":FlickerTestsImeCommon-src",
+ ],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsImeCommon",
+ ],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/IME/AndroidManifest.xml
similarity index 91%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/IME/AndroidManifest.xml
index 6bc7cbe..d6ca683 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/IME/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.ime">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.ime"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/IME/AndroidTestTemplate.xml
index ed71531..988f76f 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -83,21 +83,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/IME/OWNERS
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
rename to tests/FlickerTests/IME/OWNERS
diff --git a/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/IME/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/IME/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/IME/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
rename to tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/IME/trace_config/trace_config.textproto
similarity index 100%
rename from tests/FlickerTests/trace_config/trace_config.textproto
rename to tests/FlickerTests/IME/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/Notification/Android.bp b/tests/FlickerTests/Notification/Android.bp
new file mode 100644
index 0000000..5bed568
--- /dev/null
+++ b/tests/FlickerTests/Notification/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+android_test {
+ name: "FlickerTestsNotification",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/Notification/AndroidManifest.xml
similarity index 90%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/Notification/AndroidManifest.xml
index 6bc7cbe..d212c10 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/Notification/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.notification">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.notification"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/Notification/AndroidTestTemplate.xml
index ed71531..4036858 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -83,21 +83,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OWNERS b/tests/FlickerTests/Notification/OWNERS
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OWNERS
rename to tests/FlickerTests/Notification/OWNERS
diff --git a/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/Notification/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/Notification/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/Notification/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/Consts.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/Consts.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
rename to tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/Notification/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/Notification/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/QuickSwitch/Android.bp b/tests/FlickerTests/QuickSwitch/Android.bp
new file mode 100644
index 0000000..64f7183
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+android_test {
+ name: "FlickerTestsQuickswitch",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/QuickSwitch/AndroidManifest.xml
similarity index 90%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/QuickSwitch/AndroidManifest.xml
index 6bc7cbe..41b0cd4 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.quickswitch">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.quickswitch"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
similarity index 86%
copy from tests/FlickerTests/AndroidTestTemplate.xml
copy to tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index ed71531..797ca4e 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -83,21 +83,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/QuickSwitch/OWNERS
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
rename to tests/FlickerTests/QuickSwitch/OWNERS
diff --git a/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
rename to tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
rename to tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
rename to tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
new file mode 100644
index 0000000..8e93b5b
--- /dev/null
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+android_test {
+ name: "FlickerTestsRotation",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/Rotation/AndroidManifest.xml
similarity index 91%
copy from tests/FlickerTests/manifests/AndroidManifest.xml
copy to tests/FlickerTests/Rotation/AndroidManifest.xml
index 6bc7cbe..6bbb1f6 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/Rotation/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flicker.rotation">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -59,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.rotation"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
similarity index 86%
rename from tests/FlickerTests/AndroidTestTemplate.xml
rename to tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index ed71531..b5ea739 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -83,21 +83,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/Rotation/OWNERS
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
rename to tests/FlickerTests/Rotation/OWNERS
diff --git a/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 0000000..7b3f07e
--- /dev/null
+++ b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set>
\ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml
similarity index 63%
copy from tests/FlickerTests/manifests/AndroidManifestOther.xml
copy to tests/FlickerTests/Rotation/res/xml/network_security_config.xml
index 47749b8..4bd9ca0 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
rename to tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
rename to tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
rename to tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto
similarity index 100%
copy from tests/FlickerTests/trace_config/trace_config.textproto
copy to tests/FlickerTests/Rotation/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml b/tests/FlickerTests/manifests/AndroidManifestAppClose.xml
deleted file mode 100644
index 4cdcb90..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.close">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.close"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml b/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml
deleted file mode 100644
index 659a745..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.launch">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.launch"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestIme.xml b/tests/FlickerTests/manifests/AndroidManifestIme.xml
deleted file mode 100644
index abd03af..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestIme.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.ime">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.ime"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestNotification.xml b/tests/FlickerTests/manifests/AndroidManifestNotification.xml
deleted file mode 100644
index ad33dee..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestNotification.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.close">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.notification"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml b/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml
deleted file mode 100644
index 203035d..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.quickswitch">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.quickswitch"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestRotation.xml b/tests/FlickerTests/manifests/AndroidManifestRotation.xml
deleted file mode 100644
index 2852cf2..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestRotation.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.rotation">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.rotation"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestService.xml b/tests/FlickerTests/manifests/AndroidManifestService.xml
deleted file mode 100644
index 3a7bc509..0000000
--- a/tests/FlickerTests/manifests/AndroidManifestService.xml
+++ /dev/null
@@ -1,24 +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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.service">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.service"
- android:label="WindowManager Flicker Service Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/test-apps/app-helpers/Android.bp b/tests/FlickerTests/test-apps/app-helpers/Android.bp
new file mode 100644
index 0000000..fc4d71c
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/Android.bp
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2018 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"],
+}
+
+java_library {
+ name: "wm-flicker-common-app-helpers",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*"],
+ static_libs: [
+ "flickertestapplib",
+ "flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
+ "truth",
+ "app-helpers-core",
+ "wm-flicker-window-extensions",
+ ],
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
index e106f65..d3cee64 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
@@ -121,7 +121,7 @@
else -> null
}
if (matcher != null && matcher.find()) {
- return matcher.group(1).equals("VISIBLE")
+ return matcher.group(1) == "VISIBLE"
}
}
return false
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
similarity index 98%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index da51eff..73cc2f2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -250,11 +250,12 @@
launchedAppComponentMatcherOverride,
action,
stringExtras,
- waitConditionsBuilder = wmHelper
- .StateSyncBuilder()
- .add(ConditionsFactory.isWMStateComplete())
- .withAppTransitionIdle()
- .add(ConditionsFactory.hasPipWindow())
+ waitConditionsBuilder =
+ wmHelper
+ .StateSyncBuilder()
+ .add(ConditionsFactory.isWMStateComplete())
+ .withAppTransitionIdle()
+ .add(ConditionsFactory.hasPipWindow())
)
wmHelper
@@ -265,8 +266,7 @@
}
/** Expand the PIP window back to full screen via intent and wait until the app is visible */
- fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
- launchViaIntent(wmHelper)
+ fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper)
fun changeAspectRatio() {
val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
rename to tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
new file mode 100644
index 0000000..afaa3f0
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -0,0 +1,36 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "ConcurrentMultiSessionImeTest",
+ srcs: ["src/**/*.java"],
+ libs: ["android.test.runner"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "platform-test-annotations",
+ "platform-test-rules",
+ "truth",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ sdk_version: "current",
+
+ // Store test artifacts in separated directories for easier debugging.
+ per_testcase_directory: true,
+}
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
new file mode 100644
index 0000000..0defe5b
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.inputmethod.multisessiontest">
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.inputmethod.multisessiontest"></instrumentation>
+</manifest>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
new file mode 100644
index 0000000..fd598c5
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<configuration description="Config for Concurrent Multi-Session IME tests">
+ <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+ type="module_controller">
+ <option name="required-feature" value="android.software.input_methods" />
+
+ <!-- Currently enabled to automotive only -->
+ <option name="required-feature" value="android.hardware.type.automotive" />
+ </object>
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-install-mode" value="FULL" />
+ <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.inputmethod.multisessiontest" />
+ </test>
+</configuration>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
new file mode 100644
index 0000000..b66ceba
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.inputmethod.multisessiontest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ConcurrentMultiUserTest {
+
+ @Before
+ public void doBeforeEachTest() {
+ // No op
+ }
+
+ @Test
+ public void behaviorBeingTested_expectedResult() {
+ // Sample test
+ Context context =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertThat(context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)).isTrue();
+ assertThat(context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INPUT_METHODS)).isTrue();
+ }
+}
diff --git a/tests/inputmethod/OWNERS b/tests/inputmethod/OWNERS
new file mode 100644
index 0000000..6bb4b17
--- /dev/null
+++ b/tests/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py
index c07a98a..04a5528 100755
--- a/tools/fonts/update_font_metadata.py
+++ b/tools/fonts/update_font_metadata.py
@@ -19,7 +19,7 @@
args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision')
args = args_parser.parse_args()
- font = ttLib.TTFont(args.input)
+ font = ttLib.TTFont(args.input, recalcTimestamp=False)
update_font_revision(font, args.revision)
font.save(args.output)
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 d749f07..12c7841 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
@@ -20,6 +20,7 @@
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@@ -197,9 +198,9 @@
if (b == null) {
nativeWriteInt(nativePtr, -1);
} else {
- final var alignedSize = align4(b.length);
+ final var alignedSize = align4(len);
- nativeWriteInt(nativePtr, b.length);
+ nativeWriteInt(nativePtr, len);
p.ensureMoreCapacity(alignedSize);
@@ -280,6 +281,7 @@
+ data.length + " given=" + destLen);
return false;
}
+ System.arraycopy(data, 0, dest, 0, data.length);
return true;
}
@@ -289,7 +291,12 @@
return null;
}
var p = getInstance(nativePtr);
- p.ensureDataAvailable(size);
+ try {
+ p.ensureDataAvailable(align4(size));
+ } catch (Exception e) {
+ System.err.println(e.toString());
+ return null;
+ }
var bytes = new byte[size];
System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size);
@@ -301,7 +308,10 @@
public static int nativeReadInt(long nativePtr) {
var p = getInstance(nativePtr);
- p.ensureDataAvailable(Integer.BYTES);
+ if (p.mSize - p.mPos < 4) {
+ // Match native impl that returns "0" when not enough data
+ return 0;
+ }
var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24)
| ((p.mBuffer[p.mPos++] & 0xff) << 16)
@@ -341,11 +351,16 @@
}
public static byte[] nativeMarshall(long nativePtr) {
- throw new RuntimeException("Not implemented yet");
+ var p = getInstance(nativePtr);
+ return Arrays.copyOf(p.mBuffer, p.mSize);
}
public static void nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length) {
- throw new RuntimeException("Not implemented yet");
+ var p = getInstance(nativePtr);
+ p.ensureMoreCapacity(length);
+ System.arraycopy(data, offset, p.mBuffer, p.mPos, length);
+ p.mPos += length;
+ p.updateSize();
}
public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
throw new RuntimeException("Not implemented yet");
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index a51bdcf..1bcf364 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -22,6 +22,8 @@
import org.objectweb.asm.Type
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.FieldNode
+import org.objectweb.asm.tree.MethodNode
/** Name of the class initializer method. */
@@ -175,3 +177,93 @@
else -> false
}
}
+
+fun ClassNode.isEnum(): Boolean {
+ return (this.access and Opcodes.ACC_ENUM) != 0
+}
+
+fun ClassNode.isAnnotation(): Boolean {
+ return (this.access and Opcodes.ACC_ANNOTATION) != 0
+}
+
+fun ClassNode.isSynthetic(): Boolean {
+ return (this.access and Opcodes.ACC_SYNTHETIC) != 0
+}
+
+fun MethodNode.isSynthetic(): Boolean {
+ return (this.access and Opcodes.ACC_SYNTHETIC) != 0
+}
+
+fun FieldNode.isEnum(): Boolean {
+ return (this.access and Opcodes.ACC_ENUM) != 0
+}
+
+fun FieldNode.isSynthetic(): Boolean {
+ return (this.access and Opcodes.ACC_SYNTHETIC) != 0
+}
+
+/*
+
+Dump of the members of TinyFrameworkEnumSimple:
+
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple keep
+ field Cat keep (ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM)
+ field Dog keep
+ field $VALUES keep (ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC)
+
+ method values ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; keep
+ ^- NOT synthetic (ACC_PUBLIC, ACC_STATIC)
+ method valueOf (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; keep
+ ^- NOT synthetic (ACC_PUBLIC, ACC_STATIC)
+ method <init> (Ljava/lang/String;I)V keep
+ ^- NOT synthetic (ACC_PRIVATE)
+
+ method $values ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; keep
+ (ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC)
+ method <clinit> ()V keep
+
+Dump of the members of TinyFrameworkEnumSimple:
+
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex keep
+ field RED keep
+ field BLUE keep
+ field GREEN keep
+ field mLongName keep
+ field mShortName keep
+ field $VALUES keep
+ method values ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; keep
+ method valueOf (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; keep
+ method <init> (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V keep
+ method getLongName ()Ljava/lang/String; keep
+ method getShortName ()Ljava/lang/String; keep
+ method $values ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; keep
+ method <clinit> ()V keep
+
+ */
+
+fun isAutoGeneratedEnumMember(mn: MethodNode): Boolean {
+ if (mn.isSynthetic()) {
+ return true
+ }
+ if (mn.name == "<init>" && mn.desc == "(Ljava/lang/String;I)V") {
+ return true
+ }
+ if (mn.name == "<clinit>" && mn.desc == "()V") {
+ return true
+ }
+ if (mn.name == "values" && mn.desc.startsWith("()")) {
+ return true
+ }
+ if (mn.name == "valueOf" && mn.desc.startsWith("(Ljava/lang/String;)")) {
+ return true
+ }
+
+ return false
+}
+
+fun isAutoGeneratedEnumMember(fn: FieldNode): Boolean {
+ if (fn.isSynthetic() || fn.isEnum()) {
+ return true
+ }
+ return false
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 07a023c..9c0fa69 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -22,6 +22,9 @@
import com.android.hoststubgen.asm.isAnonymousInnerClass
import com.android.hoststubgen.log
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isAnnotation
+import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
+import com.android.hoststubgen.asm.isEnum
import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
import org.objectweb.asm.tree.ClassNode
@@ -57,18 +60,8 @@
override fun getPolicyForClass(className: String): FilterPolicyWithReason {
val fallback = super.getPolicyForClass(className)
- // TODO: This check should be cached.
val cn = classes.getClass(className)
- if (cn.superName == "java/lang/Enum" &&
- fallback.policy == FilterPolicy.Keep) {
- return FilterPolicy.KeepClass.withReason("enum")
- }
- if (cn.interfaces.contains("java/lang/annotation/Annotation") &&
- fallback.policy == FilterPolicy.Keep) {
- return FilterPolicy.KeepClass.withReason("annotation")
- }
-
// Use the implicit policy, if any.
getClassImplicitPolicy(className, cn)?.let { return it }
@@ -95,16 +88,78 @@
}
}
+ val cn = classes.getClass(className)
+
// If we throw from the static initializer, the class would be useless, so we convert it
// "keep" instead.
- if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC &&
- fallback.policy == FilterPolicy.Throw) {
+ // Unless it's an enum -- in that case, the below code would handle it.
+ if (!cn.isEnum() &&
+ fallback.policy == FilterPolicy.Throw &&
+ methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
// TODO Maybe show a warning?? But that'd be too noisy with --default-throw.
return FilterPolicy.Ignore.withReason(
"'throw' on static initializer is handled as 'ignore'" +
" [original throw reason: ${fallback.reason}]")
}
+ val classPolicy = super.getPolicyForClass(className)
+
+ log.d("Class ${cn.name} Class policy: $classPolicy")
+ if (classPolicy.policy.needsInImpl) {
+ // Do it only when the class needs to be kept...
+
+ // Member policy should be "keep" or "stub".
+ val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
+
+ // Keep (or stub) the generated enum members.
+ if (cn.isEnum()) {
+ classes.findMethod(className, methodName, descriptor)?.let { mn ->
+ if (isAutoGeneratedEnumMember(mn)) {
+ return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
+ }
+ }
+ }
+
+ // Keep (or stub) all members of annotations.
+ if (cn.isAnnotation()) {
+ return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
+ }
+ }
+
+ return fallback
+ }
+
+ override fun getPolicyForField(
+ className: String,
+ fieldName: String
+ ): FilterPolicyWithReason {
+ val fallback = super.getPolicyForField(className, fieldName)
+
+ val cn = classes.getClass(className)
+ val classPolicy = super.getPolicyForClass(className)
+
+ log.d("Class ${cn.name} Class policy: $classPolicy")
+ if (classPolicy.policy.needsInImpl) {
+ // Do it only when the class needs to be kept...
+
+ // Member policy should be "keep" or "stub".
+ val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
+
+ // Keep (or stub) the generated enum members.
+ if (cn.isEnum()) {
+ classes.findField(className, fieldName)?.let { fn ->
+ if (isAutoGeneratedEnumMember(fn)) {
+ return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
+ }
+ }
+ }
+
+ // Keep (or stub) all members of annotations.
+ if (cn.isAnnotation()) {
+ return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
+ }
+ }
+
return fallback
}
}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index f627c6e..78a4fa6 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -879,6 +879,314 @@
android.hosttest.annotation.HostSideTestStub
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+ Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 6, methods: 7, attributes: 3
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private final java.lang.String mLongName;
+ descriptor: Ljava/lang/String;
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private final java.lang.String mShortName;
+ descriptor: Ljava/lang/String;
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+ x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+ x: areturn
+ LineNumberTable:
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: aload_0
+ x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+ x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 name Ljava/lang/String;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+ descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=5, args_size=5
+ x: aload_0
+ x: aload_1
+ x: iload_2
+ x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+ x: aload_0
+ x: aload_3
+ x: putfield #x // Field mLongName:Ljava/lang/String;
+ x: aload_0
+ x: aload 4
+ x: putfield #x // Field mShortName:Ljava/lang/String;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 18 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ 0 18 3 longName Ljava/lang/String;
+ 0 18 4 shortName Ljava/lang/String;
+ Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getLongName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mLongName:Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getShortName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mShortName:Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_3
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: iconst_0
+ x: getstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: dup
+ x: iconst_1
+ x: getstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: dup
+ x: iconst_2
+ x: getstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=6, locals=0, args_size=0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String RED
+ x: iconst_0
+ x: ldc #x // String Red
+ x: ldc #x // String R
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String GREEN
+ x: iconst_1
+ x: ldc #x // String Green
+ x: ldc #x // String G
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String BLUE
+ x: iconst_2
+ x: ldc #x // String Blue
+ x: ldc #x // String B
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: return
+ LineNumberTable:
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+ Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 3, methods: 5, attributes: 3
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+ x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+ x: areturn
+ LineNumberTable:
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: aload_0
+ x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+ x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 name Ljava/lang/String;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+ descriptor: (Ljava/lang/String;I)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=3, args_size=3
+ x: aload_0
+ x: aload_1
+ x: iload_2
+ x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 7 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ Signature: #x // ()V
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_2
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: iconst_0
+ x: getstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: aastore
+ x: dup
+ x: iconst_1
+ x: getstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: aastore
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: ldc #x // String CAT
+ x: iconst_0
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V
+ x: putstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: ldc #x // String DOG
+ x: iconst_1
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V
+ x: putstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: return
+ LineNumberTable:
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index d7f0149..df63815 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -446,6 +446,230 @@
android.hosttest.annotation.HostSideTestStub
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+ Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 4, methods: 7, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+ descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=5, args_size=5
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getLongName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getShortName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+ Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 3, methods: 5, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+ descriptor: (Ljava/lang/String;I)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=3, args_size=3
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()V
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 131e0b1..2218d8d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -798,6 +798,324 @@
android.hosttest.annotation.HostSideTestStub
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+ Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 6, methods: 7, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private final java.lang.String mLongName;
+ descriptor: Ljava/lang/String;
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private final java.lang.String mShortName;
+ descriptor: Ljava/lang/String;
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+ x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+ x: areturn
+ LineNumberTable:
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: aload_0
+ x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+ x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 name Ljava/lang/String;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+ descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=5, args_size=5
+ x: aload_0
+ x: aload_1
+ x: iload_2
+ x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+ x: aload_0
+ x: aload_3
+ x: putfield #x // Field mLongName:Ljava/lang/String;
+ x: aload_0
+ x: aload 4
+ x: putfield #x // Field mShortName:Ljava/lang/String;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 18 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ 0 18 3 longName Ljava/lang/String;
+ 0 18 4 shortName Ljava/lang/String;
+ Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getLongName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mLongName:Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getShortName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: getfield #x // Field mShortName:Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_3
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: iconst_0
+ x: getstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: dup
+ x: iconst_1
+ x: getstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: dup
+ x: iconst_2
+ x: getstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=6, locals=0, args_size=0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String RED
+ x: iconst_0
+ x: ldc #x // String Red
+ x: ldc #x // String R
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String GREEN
+ x: iconst_1
+ x: ldc #x // String Green
+ x: ldc #x // String G
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String BLUE
+ x: iconst_2
+ x: ldc #x // String Blue
+ x: ldc #x // String B
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: return
+ LineNumberTable:
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+ Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 3, methods: 5, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=1, locals=0, args_size=0
+ x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+ x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+ x: areturn
+ LineNumberTable:
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: aload_0
+ x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+ x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 10 0 name Ljava/lang/String;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+ descriptor: (Ljava/lang/String;I)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=3, args_size=3
+ x: aload_0
+ x: aload_1
+ x: iload_2
+ x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 7 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ Signature: #x // ()V
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_2
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: iconst_0
+ x: getstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: aastore
+ x: dup
+ x: iconst_1
+ x: getstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: aastore
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: ldc #x // String CAT
+ x: iconst_0
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V
+ x: putstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: ldc #x // String DOG
+ x: iconst_1
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V
+ x: putstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: return
+ LineNumberTable:
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index d7f0149..df63815 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -446,6 +446,230 @@
android.hosttest.annotation.HostSideTestStub
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+ Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 4, methods: 7, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+ descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=5, args_size=5
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getLongName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getShortName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+ Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 3, methods: 5, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+ descriptor: (Ljava/lang/String;I)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=3, locals=3, args_size=3
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ Signature: #x // ()V
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 3318c7d..3ac9c6a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -1046,6 +1046,390 @@
android.hosttest.annotation.HostSideTestStub
x: #x()
android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+ Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 6, methods: 7, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private final java.lang.String mLongName;
+ descriptor: Ljava/lang/String;
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private final java.lang.String mShortName;
+ descriptor: Ljava/lang/String;
+ flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestKeep
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String values
+ x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+ x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+ x: areturn
+ LineNumberTable:
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String valueOf
+ x: ldc #x // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: aload_0
+ x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+ x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 name Ljava/lang/String;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+ descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=4, locals=5, args_size=5
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String <init>
+ x: ldc #x // String (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: iload_2
+ x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+ x: aload_0
+ x: aload_3
+ x: putfield #x // Field mLongName:Ljava/lang/String;
+ x: aload_0
+ x: aload 4
+ x: putfield #x // Field mShortName:Ljava/lang/String;
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 18 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ 11 18 3 longName Ljava/lang/String;
+ 11 18 4 shortName Ljava/lang/String;
+ Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getLongName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String getLongName
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: getfield #x // Field mLongName:Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public java.lang.String getShortName();
+ descriptor: ()Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String getShortName
+ x: ldc #x // String ()Ljava/lang/String;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: getfield #x // Field mShortName:Ljava/lang/String;
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String $values
+ x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_3
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: iconst_0
+ x: getstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: dup
+ x: iconst_1
+ x: getstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: dup
+ x: iconst_2
+ x: getstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: aastore
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=6, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String RED
+ x: iconst_0
+ x: ldc #x // String Red
+ x: ldc #x // String R
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String GREEN
+ x: iconst_1
+ x: ldc #x // String Green
+ x: ldc #x // String G
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+ x: dup
+ x: ldc #x // String BLUE
+ x: iconst_2
+ x: ldc #x // String Blue
+ x: ldc #x // String B
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+ x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+ x: return
+ LineNumberTable:
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+ Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+ minor version: 0
+ major version: 61
+ flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+ this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ super_class: #x // java/lang/Enum
+ interfaces: 0, fields: 3, methods: 5, attributes: 4
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+ descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+ RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
+
+ private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+ descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: ldc #x // String values
+ x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+ x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+ x: areturn
+ LineNumberTable:
+
+ public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+ descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: ldc #x // String valueOf
+ x: ldc #x // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: aload_0
+ x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+ x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: areturn
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 10 0 name Ljava/lang/String;
+
+ private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+ descriptor: (Ljava/lang/String;I)V
+ flags: (0x0002) ACC_PRIVATE
+ Code:
+ stack=4, locals=3, args_size=3
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: ldc #x // String <init>
+ x: ldc #x // String (Ljava/lang/String;I)V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: aload_1
+ x: iload_2
+ x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 7 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ Signature: #x // ()V
+
+ private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+ descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: ldc #x // String $values
+ x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: iconst_2
+ x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: iconst_0
+ x: getstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: aastore
+ x: dup
+ x: iconst_1
+ x: getstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: aastore
+ x: areturn
+ LineNumberTable:
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: ldc #x // String CAT
+ x: iconst_0
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V
+ x: putstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+ x: dup
+ x: ldc #x // String DOG
+ x: iconst_1
+ x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V
+ x: putstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+ x: return
+ LineNumberTable:
+}
+Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+ x: #x()
+ android.hosttest.annotation.HostSideTestStub
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
Compiled from "TinyFrameworkExceptionTester.java"
public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
new file mode 100644
index 0000000..51f4818
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
@@ -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.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestKeep;
+import android.hosttest.annotation.HostSideTestStub;
+
+@HostSideTestStub
+public enum TinyFrameworkEnumComplex {
+ @HostSideTestStub
+ RED("Red", "R"),
+ @HostSideTestStub
+ GREEN("Green", "G"),
+ @HostSideTestStub
+ BLUE("Blue", "B");
+
+ @HostSideTestKeep
+ private final String mLongName;
+
+ @HostSideTestKeep
+ private final String mShortName;
+
+ @HostSideTestStub
+ TinyFrameworkEnumComplex(String longName, String shortName) {
+ mLongName = longName;
+ mShortName = shortName;
+ }
+
+ @HostSideTestStub
+ public String getLongName() {
+ return mLongName;
+ }
+
+ @HostSideTestStub
+ public String getShortName() {
+ return mShortName;
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
new file mode 100644
index 0000000..f440d86
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
@@ -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.
+ */
+package com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestStub;
+
+@HostSideTestStub
+public enum TinyFrameworkEnumSimple {
+ @HostSideTestStub
+ CAT,
+ @HostSideTestStub
+ DOG,
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 29aabc7..ecb181b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -199,4 +199,43 @@
public void testPackageRedirect() throws Exception {
assertThat(TinyFrameworkPackageRedirect.foo(1)).isEqualTo(1);
}
+
+ @Test
+ public void testEnumSimple() throws Exception {
+ assertThat(TinyFrameworkEnumSimple.CAT.ordinal()).isEqualTo(0);
+ assertThat(TinyFrameworkEnumSimple.CAT.name()).isEqualTo("CAT");
+
+ assertThat(TinyFrameworkEnumSimple.DOG.ordinal()).isEqualTo(1);
+ assertThat(TinyFrameworkEnumSimple.DOG.name()).isEqualTo("DOG");
+
+ assertThat(TinyFrameworkEnumSimple.valueOf("DOG").ordinal()).isEqualTo(1);
+
+ assertThat(TinyFrameworkEnumSimple.values()).isEqualTo(
+ new TinyFrameworkEnumSimple[] {
+ TinyFrameworkEnumSimple.CAT,
+ TinyFrameworkEnumSimple.DOG,
+ }
+ );
+ }
+
+ @Test
+ public void testEnumComplex() throws Exception {
+ assertThat(TinyFrameworkEnumComplex.RED.ordinal()).isEqualTo(0);
+ assertThat(TinyFrameworkEnumComplex.RED.name()).isEqualTo("RED");
+
+ assertThat(TinyFrameworkEnumComplex.RED.getShortName()).isEqualTo("R");
+
+ assertThat(TinyFrameworkEnumComplex.GREEN.ordinal()).isEqualTo(1);
+ assertThat(TinyFrameworkEnumComplex.GREEN.name()).isEqualTo("GREEN");
+
+ assertThat(TinyFrameworkEnumComplex.valueOf("BLUE").ordinal()).isEqualTo(2);
+
+ assertThat(TinyFrameworkEnumComplex.values()).isEqualTo(
+ new TinyFrameworkEnumComplex[] {
+ TinyFrameworkEnumComplex.RED,
+ TinyFrameworkEnumComplex.GREEN,
+ TinyFrameworkEnumComplex.BLUE,
+ }
+ );
+ }
}