diff --git a/Ravenwood.bp b/Ravenwood.bp index 1582266203c6a13e6c0a11b4e7434726dcc8d27c..3310898ccfb5d94b5b1e800126f5bd923cb03e35 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -77,10 +77,17 @@ android_ravenwood_libgroup { "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/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java index 5fc77451e832761eac4f97813d2a537ec98399a1..e08200b055d86406bdc3c42418214f2f68359ed2 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; 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 class AppStateTrackerImpl implements AppStateTracker { 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 95f901c4a365e7cb5cd6ef6462f0467ae4db8cd3..b8397d2cd1b47fab0410b7ee7d536648ebc11e98 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.EconomyManager; 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 class InternalResourceService extends SystemService { 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/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index ee9c464219d9e1b4ab681ea113ff821b98ff656f..2d235331a672c2587a76bfe7d5f20b7b750f8d85 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -142,13 +142,22 @@ int main(int argc, char** argv) 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 14869dba96460722fa27b63f57403251bd18b934..323f9e9cd7c6b0e227c1bf1c50bb62d50b963e7c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4464,6 +4464,7 @@ package android.app { 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); @@ -13973,6 +13974,7 @@ package android.database { 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); @@ -13998,6 +14000,7 @@ package android.database { 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; @@ -14129,6 +14132,7 @@ package android.database { 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 { @@ -14196,6 +14200,7 @@ package android.database { 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); @@ -14220,6 +14225,7 @@ package android.database { 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); } @@ -22213,6 +22219,9 @@ package android.media { 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 @@ -22725,6 +22734,9 @@ package android.media { 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 { @@ -22949,6 +22961,9 @@ package android.media { 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(); } @@ -23022,6 +23037,9 @@ package android.media { 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 @@ -23029,6 +23047,9 @@ package android.media { 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 { @@ -31918,6 +31939,7 @@ package android.opengl { 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 @@ -47459,6 +47481,7 @@ package android.text { 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); } @@ -48924,6 +48947,7 @@ package android.util { 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); @@ -48937,6 +48961,7 @@ package android.util { 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(); @@ -48967,6 +48992,7 @@ package android.util { 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(); @@ -52834,6 +52860,7 @@ package android.view { 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(); @@ -52875,6 +52902,7 @@ package android.view { 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); @@ -55058,12 +55086,14 @@ package android.view.inputmethod { 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); @@ -55071,6 +55101,7 @@ package android.view.inputmethod { 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 { @@ -55407,6 +55438,7 @@ package android.view.inputmethod { 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(); @@ -55415,18 +55447,29 @@ package android.view.inputmethod { 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 { @@ -57993,6 +58036,7 @@ package android.widget { 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/lint-baseline.txt b/core/api/lint-baseline.txt index 1e6aa498f4176e109364bf03147222fa38248712..483d2a4d26b241e9d7ff5d6c28bdd2184c927530 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -1,4 +1,40 @@ // Baseline format: 1.0 +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[]): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindBlob(int, byte[], int, int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindDouble(int, double): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindInt(int, int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindLong(int, long): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindNull(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#bindText(int, String): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnBlob(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnDouble(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnInt(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLength(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnLong(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnName(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnText(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#getColumnType(int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#readColumnBlob(int, byte[], int, int, int): + Methods must not throw unchecked exceptions +BannedThrow: android.database.sqlite.SQLiteRawStatement#step(): + Methods must not throw unchecked exceptions + + BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED: Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED: diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 07374963bea05cc45f60536f93f14b0683956287..df466ab9b5bbe30bbc047f8c3e26ec4633394e2a 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -144,7 +144,6 @@ package android.content.pm { } 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 739fdc59ed5d92dff52eff327df5a50c37bae0e1..eefa36eb4f2d0bbbe3ab92b1e078b3b6ac0cc7b2 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3916,6 +3916,7 @@ package android.content.pm { 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); @@ -9733,6 +9734,7 @@ package android.nfc.cardemulation { 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); @@ -9743,6 +9745,7 @@ package android.nfc.cardemulation { 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; } @@ -10225,6 +10228,7 @@ package android.os { 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-lint-baseline.txt b/core/api/test-lint-baseline.txt index 559db514dabf29c4fd99450903e52bbac1c13ceb..3a91e25de9e6d9a6ee5c090d4f2278fa9753397c 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -1,4 +1,8 @@ // Baseline format: 1.0 +BannedThrow: android.os.vibrator.persistence.VibrationXmlSerializer#serialize(android.os.VibrationEffect, java.io.Writer): + Methods must not throw unchecked exceptions + + BroadcastBehavior: android.app.AlarmManager#ACTION_NEXT_ALARM_CLOCK_CHANGED: Field 'ACTION_NEXT_ALARM_CLOCK_CHANGED' is missing @BroadcastBehavior BroadcastBehavior: android.app.AlarmManager#ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED: diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index a99dfa6054079a4909ec1aeb713223220b521dcd..3d54449971077fd5904ba73f072d28ae4d77ce59 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -8357,9 +8357,7 @@ public class AppOpsManager { */ 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 @@ public class AppOpsManager { } } - 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 @@ public class AppOpsManager { @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 @@ public class AppOpsManager { @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 @@ public class AppOpsManager { } } - 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 class AppOpsManager { 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 a3de8faa12734f1880e29ee9e23ca1a33472c607..43023fe9c2abb23c982652ad4fd158ad26646d89 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -26,12 +26,11 @@ import android.util.SparseArray; 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 @@ public abstract class AppOpsManagerInternal { * 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 @@ public abstract class AppOpsManagerInternal { * 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 @@ public abstract class AppOpsManagerInternal { * * @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 @@ public abstract class AppOpsManagerInternal { * @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 @@ public abstract class AppOpsManagerInternal { * * @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 @@ public abstract class AppOpsManagerInternal { * * @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/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 1f8784bf72dd4124097bb475af6c712baff27151..df6badcffe8e9a0b2f254843d48946fc8ee9ef76 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -269,7 +269,10 @@ interface IActivityTaskManager { * 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/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 27f6a266597cd4a502a918a3b4fd3b8b509fef9c..ec181dac6b36b845d4e1fff3d13a3fcb93e9a86d 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -155,6 +155,22 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW mInteractionHandler = getHandler(handler); } + /** + * @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)} @@ -921,17 +937,31 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW 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 c2bc974a42ae5d0a06b923557a053783523ba351..4b2cee698df21902a915feb6066a5bfdc4a8f35e 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -234,12 +234,6 @@ public final class AttributionSource implements Parcelable { return withToken(sDefaultToken); } - /** @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(), diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e2a5747a4bcb21ea286449d52d58f74a3ca3b01d..72e1066395448fea597c723d098f2660b542dfec 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -6279,9 +6279,9 @@ public abstract class PackageManager { * @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/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig index 6b78d05c848d9fd4cbae3542ff6225c5132ade03..5c5083a691be256a4420667a5670fb125a6e8761 100644 --- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig +++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig @@ -6,3 +6,10 @@ flag { 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 dbb312720373217b6647d3445d7a3c0c5c459473..19ba6a1d22ede4647579630ce58ae9e601809cdb 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -23,6 +23,7 @@ import android.content.Context; 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 @@ public class NetworkStack { 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 9cf8c4ddc53bdb14d39a630a0d46ad56c776e594..597c948bd51516cf2323883f52549ab6813684ab 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -129,7 +129,7 @@ public final class ApduServiceInfo implements Parcelable { /** * State of the service for CATEGORY_OTHER selection */ - private boolean mOtherServiceSelectionState; + private boolean mOtherServiceEnabled; /** * @hide @@ -150,10 +150,10 @@ public final class ApduServiceInfo implements Parcelable { 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 final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { this.mBannerResourceId = bannerResource; this.mUid = uid; this.mSettingsActivityName = settingsActivityName; - this.mOtherServiceSelectionState = isSelected; + this.mOtherServiceEnabled = isEnabled; } @@ -372,7 +372,7 @@ public final class ApduServiceInfo implements Parcelable { // Set uid mUid = si.applicationInfo.uid; - mOtherServiceSelectionState = false; // support other category + mOtherServiceEnabled = false; // support other category } @@ -744,7 +744,7 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { 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 @@ public final class ApduServiceInfo implements Parcelable { /** - * @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 3db1cb0500f97f1cbf71a7104a57bfe11831c99a..995622004fa6b7e6fdc5cd0c823c00ece131ea27 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -333,6 +333,12 @@ public class Binder implements IBinder { @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 class Binder implements IBinder { */ 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 @@ public class Binder implements IBinder { 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 352c9d2ba81f1186a389a57a7e7325c82e4bfe22..86628d95b25ae398ac29b58200d827307c655d8c 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -1558,7 +1558,7 @@ public final class Parcel { 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); @@ -2956,6 +2956,14 @@ public final class Parcel { } } + /** @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; @@ -3039,6 +3047,12 @@ public final class Parcel { } } + /** @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/security/flags.aconfig b/core/java/android/security/flags.aconfig index 0133bd8221828a1130b0f14a71791d7c59d7de05..9bdd0c2db7795d7572da66b72de2838023d48da6 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -36,3 +36,10 @@ flag { 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/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 0d5704eed4b300ffe214eb67af99b754a9d1803a..1acc384b18dac1fbbde209ddfe2d5c81ed082ee6 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -18,6 +18,7 @@ package android.view; 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 class InsetsSource implements Parcelable { 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/widget/OWNERS b/core/java/android/widget/OWNERS index 7f0a651c6420b7720941c454938dbe56f29b183a..e20357fa2dd9abe0132accf7d47aa34b629a07ff 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -12,6 +12,6 @@ per-file TextView*,Edit*,Selection* = file:../text/OWNERS 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 d84330828bf7238ba4a6f0d5cda70c1ae83de3de..9b396aeea9eb2c5fd69cf6ec33245fb94284def9 100644 --- a/core/java/android/widget/RemoteCollectionItemsAdapter.java +++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java @@ -18,6 +18,7 @@ package android.widget; 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.ColorResources; import android.widget.RemoteViews.InteractionHandler; import android.widget.RemoteViews.RemoteCollectionItems; -import com.android.internal.R; - import java.util.stream.IntStream; /** @@ -178,40 +177,14 @@ class RemoteCollectionItemsAdapter extends BaseAdapter { 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 403b403be9612c807a11073e77e81b6175ba3915..a919c0079cea1b39b4f8095de770b8679e13a9d1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -6958,13 +6958,13 @@ public class RemoteViews implements Parcelable, Filter { 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 61a7599e8f730fc415dea4c8a40de0c1c0d89a6e..2f28a8704cd3d5d2e18b08d91fa8b6bf363d6efd 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -368,7 +368,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback * 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; @@ -407,11 +407,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback return loadingTextView; } - @Override - protected Context getRemoteContextEnsuringCorrectCachedApkPath() { - return null; - } - @Override protected View getErrorView() { // Use the default loading view as the error view. diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index 24dc6dbfede846d968c543f2061fe57c9ba0502a..0ad6c99422b81a6cb904c0dbdfcb3c603e6cbdb2 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -29,3 +29,10 @@ flag { 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 1b05982a7569d16a15bc25ce4bbf321489769568..492e2ac7cc288f3f10e9f4a37b56a2be954f59e0 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -42,22 +42,18 @@ import com.android.internal.app.MessageSamplingConfig; // 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 @@ interface IAppOpsService { 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/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 9d88a2341fad2efc309be0832921e5ce786ce916..d85227f1a0a8254c874f97113790c6d66173116b 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -70,9 +70,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { 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/jni/Android.bp b/core/jni/Android.bp index 3795fc8594ae87c2cdbb41b10bda42899ceba1a8..440a33244a62c6fb140517642e4ec7686030a329 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -43,7 +43,10 @@ cc_library_shared_for_libandroid_runtime { "-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 91e3c921afe6a5101656a8366c07470b732fa96a..66bda6100b319536983978424bd8e07135d3f0e0 100644 --- a/core/jni/android_content_res_ResourceTimer.cpp +++ b/core/jni/android_content_res_ResourceTimer.cpp @@ -44,9 +44,9 @@ static struct { 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 30e546cc290d33deac1ad46a4a8f588d9d813960..82570be8e329d983791a2cb319a1f9985d458bbe 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -1892,8 +1892,8 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image 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 139075907bf3f4d5bfb503155ff8dc1c71f25563..4bde87f65326ebbb61d523f0180d5483ea3bbb05 100644 --- a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp +++ b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp @@ -599,7 +599,7 @@ static jint CameraExtensionJpegProcessor_compressJpegFromYUV420p( 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 4b563d7f3bd8a88d7a896e0d1dc150e26ea408b6..7c9dae0ef306a27d1faf7e5c5e307b7c9c9c2122 100644 --- a/core/jni/android_media_AudioProductStrategies.cpp +++ b/core/jni/android_media_AudioProductStrategies.cpp @@ -88,12 +88,12 @@ static jint convertAudioProductStrategiesFromNative( 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 6440cc3952f84ed53c68c39577e218ccba1331cc..3413ededb8893d24144fa146edac17e91fa6a010 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2501,7 +2501,7 @@ static std::vector<uid_t> convertJIntArrayToUidVector(JNIEnv *env, jintArray jAr 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 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj 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 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje 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 9bd07007586c2362fb9789b1f2ad8b938b5882d7..47b4a469aa5bc3f9ee91c70dc413786e11cf0029 100644 --- a/core/jni/android_net_LocalSocketImpl.cpp +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -221,7 +221,7 @@ static int socket_write_all(JNIEnv *env, jobject object, int fd, 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 a9db91be1d5b04c352018abf83209199243ec576..e554b44233b52c3af65b123f0338a28a6e207fbd 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -265,7 +265,7 @@ jobject JHwBlob::NewObject(JNIEnv *env, size_t size) { // 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 4d8dac1daaf0eabf3cc230c367b197d2e9cea629..3539476b8ce8e80a570f970dd249ac725006b59f 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -691,7 +691,7 @@ static jboolean android_os_Parcel_hasFileDescriptorsInRange(JNIEnv* env, jclass // 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 27c4cd4d2cd1ce805789ae72b7e30531ac094a29..95bf49fe501ecaa1406b870750a2c121b1b6667c 100644 --- a/core/jni/android_os_PerformanceHintManager.cpp +++ b/core/jni/android_os_PerformanceHintManager.cpp @@ -224,7 +224,7 @@ static jintArray nativeGetThreadIds(JNIEnv* env, jclass clazz, jlong nativeSessi 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 7ab6e8f27fb27f04f79a9f3289b51d85ee297b40..7071cf2336d6725b79019a5c45755710f5f5ac67 100644 --- a/core/jni/android_util_CharsetUtils.cpp +++ b/core/jni/android_util_CharsetUtils.cpp @@ -25,7 +25,7 @@ static jint android_util_CharsetUtils_toModifiedUtf8Bytes(JNIEnv *env, jobject c // 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 @@ static jint android_util_CharsetUtils_toModifiedUtf8Bytes(JNIEnv *env, jobject c // 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 c64c21244fe0469512ee16cfe0a8e0cc6e8fe571..8d3da7b81f5bca4d349cb4b73fc76b0156ac195b 100644 --- a/core/jni/android_util_FileObserver.cpp +++ b/core/jni/android_util_FileObserver.cpp @@ -114,7 +114,7 @@ static void android_os_fileobserver_startWatching(JNIEnv* env, jobject object, j 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 41c65aec3144df268392945a9cd37c912addb6db..fef8ad7499f7ac8856da112e664201e1e2b53c79 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -140,7 +140,7 @@ static jobject createJavaVsyncEventData(JNIEnv* env, VsyncEventData vsyncEventDa 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 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla 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 98335988459a4f83df786097418fabf414d40ee4..a800e6ea60e4460b2d1579b738539150b9726332 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1950,7 +1950,7 @@ public: 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 55995df299b0d901ab1ed9a157bfa968305be6f3..bc69d1e676684635252df16c3c9c2fda9dc0b860 100644 --- a/core/jni/android_window_WindowInfosListener.cpp +++ b/core/jni/android_window_WindowInfosListener.cpp @@ -67,7 +67,7 @@ jobject fromDisplayInfo(JNIEnv* env, gui::DisplayInfo displayInfo) { 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 fromWindowInfos(JNIEnv* env, const std::vector<WindowInfo>& 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 d4f6e1868695d282d8a0cbb4ca65fcdcab8a21b1..0c39a6928391433ec34b3b364dbe1d13e0b3fd74 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 @@ static void CreateFrroFile(JNIEnv* env, jclass /*clazz*/, jstring jsFrroFilePath 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 098a4d8682690a969f181a26dd1f25467415f834..cf70c90af8be48ddd07aeba8e0d1e5873d97ea63 100644 --- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp @@ -55,7 +55,7 @@ static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector 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 dfae68429f6d12e18faa36328d1e4f39ee685199..be9013bec08eb25461b8a095857bef9f4ddc7a3e 100644 --- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp +++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp @@ -147,7 +147,7 @@ static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray se 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 @@ MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes( 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 1f29735b93a4521c1b2d862e24544b2b77f8769f..dab47e9a9e28cb95dc4c8b71a3b1f8596998ba88 100644 --- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp +++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp @@ -288,7 +288,7 @@ static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jobject sel } 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 9c1bea779201e637e0f790380a6790513ca4b2e6..7c5885adb220f67b63d6e629b5868495fdadc7bf 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1094,8 +1094,8 @@ static std::string getAppDataDirName(std::string_view parent_path, std::string_v } 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 ReloadBuildJavaConstant(JNIEnv* env, jclass build_class, const char* 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 @@ static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas 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 2b5b8f7a108e5c6b5e28ffff89b1dcaddf418524..87ab4969040ef2cef461ce5ae27fdc97238837a2 100644 --- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp +++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp @@ -76,7 +76,7 @@ class NativeCommandBuffer { 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 @@ class NativeCommandBuffer { } 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 @@ class NativeCommandBuffer { // 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 @@ class NativeCommandBuffer { 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 @@ class NativeCommandBuffer { } 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 @@ class NativeCommandBuffer { 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 @@ jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( 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 8e619a8bda48287615173ca0ed72e304674321c5..404fa39fb6ed1d609d7122e407b58fd5ce10f58d 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -593,6 +593,7 @@ message InsetsSourceProviderProto { 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 41b9f432a0ed4a2bd2b0fd2f7ee7a7ce1f47028e..e6c6d59787792842cf51e40490a60196b76fe043 100644 --- a/core/proto/android/view/insetssource.proto +++ b/core/proto/android/view/insetssource.proto @@ -30,4 +30,5 @@ message InsetsSourceProto { 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/res/values/config.xml b/core/res/res/values/config.xml index 862e537aa8c91d77a4feab2a1e2a2c6da1792218..f827d28b26fd19a7293b083d52118ed1d2ad9d23 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 8748ca1f48a5ed60ef15127aa6110604f3f7e729..2f3b5101269334dd64e244dcbac0f6135516f491 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" /> diff --git a/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt index 8e76fd22f99a8be49338628c3f00e3f54ff543fd..48c0a2de8f866d1a112c1d4192db0003b54dd5b5 100644 --- a/framework-minus-apex-ravenwood-policies.txt +++ b/framework-minus-apex-ravenwood-policies.txt @@ -71,8 +71,9 @@ class android.util.proto.WireTypeMismatchException stubclass # 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.GrowingArrayUtils stubclass 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/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java index 42dc19ce838a81568d1b2ae9d4715161e40d5169..7749394b21d315f5c351b4e44a7a97b913683da9 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 @@ public class BackAnimationBackground { 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/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java index a596cef9562a133a6d105d71188abce674f6e80e..f90e3f04ae5e4159538b7126b4430ae60007f1a4 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 android.view.RemoteAnimationTarget.MODE_OPENING; 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 @@ public class CrossActivityAnimation extends ShellBackAnimation { // 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 @@ public class CrossActivityAnimation extends ShellBackAnimation { 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 5a156740e605e21d1e220d2a640250766f6cdd14..80fc3a867d48606859e6df6bc41691e61dd4c71a 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 @@ package com.android.wm.shell.back; 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 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { 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 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { // 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 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { 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 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { 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/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 0890861596a5459cb4d2033fdb20b5855e332898..e63bbc07bc4197c20ab83770707c5a3e3d5a01e7 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 @@ public class KeyguardTransitionHandler 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 57cc28d1dde5c04a22c0817a99efffceaacb4682..8eb4a5a1a4c9513030e5142ce891f15a5ebd0590 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 @@ public class PipAnimationController { 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 c701b9581ca212e5d0eb7aa9534ca09de1d7cda8..850b06a0fb7df0ae0ee3aa87dcdd85e7f4f2de9c 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 @@ public abstract class PipContentOverlay { 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 abstract class PipContentOverlay { 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 @@ public abstract class PipContentOverlay { 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 9e8f9c68d43d6def4ba59c41fd104a364a2fb208..083cd08e9a17ef8789812bc80e56c0af61b412ec 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 018d674e54276b43adedfed7c197ac41bb21d8a8..d5b29e384c092ee3c82fa139975b3d774021c6ed 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 @@ public class PipTransition extends PipTransitionController { 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/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp index 386983ce6aae8dc43c587a1fac7418fb541cbc58..4d11dfb37c05e4a3fb5ecb5f32b2cf9ad77a6f1c 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp @@ -124,10 +124,6 @@ android_test { ":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 ad600d0bab9352a095941e70a0368ce9c99c5920..6c3172a267516051b9de76e6528ccea1be8b6797 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -46,7 +46,7 @@ namespace uirenderer { #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/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 774478669058f78c840dd3d5dfe6b8dd13704e3b..c5ffbb70213ef04ed8ac8ac8678966fb876221ad 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -187,11 +187,12 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( 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 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect 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 f0461bef170c56f97de92862b8cd2393d9220698..098a74655831ee022c89074459f2438afaa50566 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -44,8 +44,9 @@ public: 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 86096d5bd01c6fbdcf436af3a18d55fcdd2c1447..12cb69da772b964dfd0d8d468066dda52a1b5d67 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -87,7 +87,7 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( } 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 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( 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 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( 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 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect 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 284cde537ec08b9657506cd0bf6ab1a818f1cc2f..e2ea57d32cd5483ad6fced8868b1036442bed73a 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -44,8 +44,9 @@ public: 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 9e3bb79f03f39e585ba142d0460532208033727d..56b52dc2abe763bea5b25511f45b8c7971087d09 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -664,8 +664,8 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { 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)); diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 6c2cb9d71c5534c8b770b1fefd836c50af062393..9c879d5a58d1f4fe7aebba0d819368467121d100 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -61,6 +61,7 @@ public: // 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 @@ public: 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/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 22c58624870585ea64e5e6fc611e68e356a734fa..e706eb083ab66661ab0dc94a770752d2afe019a4 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -531,32 +531,21 @@ struct DestroySemaphoreInfo { 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 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) { 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 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) { 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 @@ nsecs_t VulkanManager::finishFrame(SkSurface* surface) { } // 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; } - } - skiapipeline::ShaderCache::get().onVkFrameFlushed(context); - - return submissionTime; -} - -void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { - 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.semaphore = semaphore; getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + 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::swapBuffers(): Semaphore submission failed"); + ALOGE("VulkanManager::finishFrame(): Semaphore submission failed"); mQueueWaitIdle(mGraphicsQueue); } - if (mDestroySemaphoreContext) { - destroy_semaphore(mDestroySemaphoreContext); + + skiapipeline::ShaderCache::get().onVkFrameFlushed(context); + + return drawResult; +} + +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); } - surface->presentCurrentBuffer(dirtyRect, fenceFd); - mSwapSemaphore = VK_NULL_HANDLE; - mDestroySemaphoreContext = nullptr; + surface->presentCurrentBuffer(dirtyRect, presentFence.release()); } void VulkanManager::destroySurface(VulkanSurface* surface) { @@ -753,22 +735,17 @@ status_t VulkanManager::createReleaseFence(int* nativeFence, GrDirectContext* gr 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 @@ status_t VulkanManager::createReleaseFence(int* nativeFence, GrDirectContext* gr 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 dbef7fbd51b2e4d74bb3007f8f180a8d4fdbf5bd..b92ebb3cdf71d616a27bc6162675eebac8ca7618 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 @@ public: 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 @@ private: 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/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java index 1a9ebbc56d681dbd7b69b212eba75279e88014e0..d60712674990c59f767e3c9d46a8e89bf016ea69 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 @@ public final class AudioVolumeGroup implements Parcelable { } @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 08a8d897c96eb44b0f9b7f0d20b8a1caa05282f4..2769dbc430ba94b5185c86965881e02b1a8423f8 100644 --- a/media/jni/android_media_MediaMetricsJNI.cpp +++ b/media/jni/android_media_MediaMetricsJNI.cpp @@ -127,7 +127,7 @@ jobject MediaMetricsJNI::writeMetricsToBundle( 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/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 index e914d5ce5140bc4f9729b8dc7fd4dfeee0c4f7c8..8386bc1b1ba3ddbad8c15318030cfc24ad849225 100644 --- 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 @@ -18,6 +18,9 @@ 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 @@ -29,13 +32,15 @@ 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 = "ActionButton" + override val name = "CardPage" override fun getTitle(arguments: Bundle?) = TITLE @@ -44,18 +49,21 @@ object CardPageProvider : SettingsPageProvider { RegularScaffold(title = TITLE) { SettingsCardWithIcon() SettingsCardWithoutIcon() + SampleSettingsCollapsibleCard() } } @Composable private fun SettingsCardWithIcon() { SettingsCard( - 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) {}, + 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) {}, + ) ) ) } @@ -63,10 +71,39 @@ object CardPageProvider : SettingsPageProvider { @Composable private fun SettingsCardWithoutIcon() { SettingsCard( - title = stringResource(R.string.sample_title), - text = stringResource(R.string.sample_text), - buttons = listOf( - CardButton(text = "Action") {}, + 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) {}, + ) + ) ) ) } 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 0000000000000000000000000000000000000000..d48e56464d9406caa394e84440a3dfd96c245743 --- /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 4088ffd43986d6fa775eb63835dd6c689bb3c775..47660bc44cc80283599d9c116307510b4af21d8a 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.foundation.layout.PaddingValues 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 0000000000000000000000000000000000000000..c113f4332fc38d339e98d3bfdf90892276c3795a --- /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 index 98873e037df80658a27c6ae2309ba2d93e46aa97..10e26869a76ef3221cd7d8b3f4af2e3355378d1b 100644 --- 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 @@ -16,9 +16,9 @@ package com.android.settingslib.spa.widget.card -import android.content.res.Configuration 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 @@ -37,26 +37,15 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.tooling.preview.Preview +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 -data class CardButton( - val text: String, - val isMain: Boolean = false, - val onClick: () -> Unit, -) - @Composable -fun SettingsCard( - title: String, - text: String, - imageVector: ImageVector? = null, - buttons: List<CardButton> = emptyList(), -) { +fun SettingsCard(content: @Composable ColumnScope.() -> Unit) { Card( shape = CornerExtraLarge, colors = CardDefaults.cardColors( @@ -68,16 +57,27 @@ fun SettingsCard( 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) ) { - Column( - modifier = Modifier.padding(SettingsDimension.itemPaddingStart), - verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround) - ) { - CardIcon(imageVector) - SettingsTitle(title) - SettingsBody(text) - Buttons(buttons) - } + CardIcon(model.imageVector) + SettingsTitle(model.title) + SettingsBody(model.text) + Buttons(model.buttons) } } @@ -136,28 +136,19 @@ private fun Button(button: CardButton) { } } -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -@Composable -private fun SettingsCardPreviewLight() { - SettingsCardPreview() -} - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -private fun SettingsCardPreviewDark() { - SettingsCardPreview() -} - +@UiModePreviews @Composable private fun SettingsCardPreview() { SettingsTheme { SettingsCard( - 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) {}, + 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 0000000000000000000000000000000000000000..7d1064512a644d3aef39cf6cd7dba8dd2347a734 --- /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 8cbf7cc2ea475bc4386ab7519bc9a6d0a26031e1..a9974dc7d3893d68f0b64e3cc8122477a6fa5de9 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 @@ fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> 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, @@ -109,6 +103,17 @@ fun Spinner(options: List<SpinnerOption>, selectedId: Int?, setId: (id: Int) -> } } +@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?, 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 7f1acffe7a8a416e1e803b141ac473c604632e28..f4b284362c52d1839697a79efff6599aef2e3477 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.material3.Text 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 @@ fun SettingsTitle(title: String, useMediumWeight: Boolean = false) { 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 @@ fun SettingsBody( @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 @@ fun PlaceholderTitle(title: String) { } } +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 index 0ec8507cb88bb1193acc6f287f7ee0d8adb23205..fd3ae49d93c850da812be2207763c9dd6525323e 100644 --- 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 @@ -35,8 +35,10 @@ class SettingsCardTest { fun settingsCard_titleDisplayed() { composeTestRule.setContent { SettingsCard( - title = TITLE, - text = "", + CardModel( + title = TITLE, + text = "", + ) ) } @@ -47,8 +49,10 @@ class SettingsCardTest { fun settingsCard_textDisplayed() { composeTestRule.setContent { SettingsCard( - title = "", - text = TEXT, + CardModel( + title = "", + text = TEXT, + ) ) } @@ -59,11 +63,13 @@ class SettingsCardTest { fun settingsCard_buttonDisplayed() { composeTestRule.setContent { SettingsCard( - title = "", - text = "", - buttons = listOf( - CardButton(text = TEXT) {} - ), + CardModel( + title = "", + text = "", + buttons = listOf( + CardButton(text = TEXT) {} + ), + ) ) } @@ -75,11 +81,13 @@ class SettingsCardTest { var buttonClicked = false composeTestRule.setContent { SettingsCard( - title = "", - text = "", - buttons = listOf( - CardButton(text = TEXT) { buttonClicked = true } - ), + CardModel( + title = "", + text = "", + buttons = listOf( + CardButton(text = TEXT) { buttonClicked = true } + ), + ) ) } 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 0000000000000000000000000000000000000000..efe1c701b1f6d83a67fbc7dd38735e26274f1503 --- /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/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index f0741061a051d858e6dcf1d3cd73f2ee908433a7..f03263b71138e1efb2a1a98ab8c88a2257ff0d3b 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.ColorFilter; 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 @@ public class Utils { 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 a93524f6d87355824d1a33fbdb92abab093f6bb1..51164e8365909a079bb97c968e818cc3dad0dcec 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 class LeAudioProfile implements LocalBluetoothProfile { } 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/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 29846ac30909a9da5c84bbfe64354a8bc03e5b4e..a88a9c72e4a91012ebe507a61edf6216605d3236 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.app.ActivityManager; 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.media.AudioManager; 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 android.text.TextUtils; 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 @@ public class UtilsTest { @Mock private UsbPortStatus mUsbPortStatus; + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -425,8 +430,38 @@ public class UtilsTest { } @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 class UtilsTest { public void containsIncompatibleChargers_emptyComplianceWarnings_returnFalse() { setupIncompatibleCharging(); when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]); - assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse(); } @@ -476,7 +510,7 @@ public class UtilsTest { } private void setupIncompatibleCharging() { - setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY); } private void setupIncompatibleCharging(int complianceWarningType) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index 6bc27160c0a867e3def9f8880c589a45618fbc70..6ff36d43f91f3b32caddaa208c871ddd5e3bed08 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.app.ActivityManager; 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 @@ public final class DeviceConfigService extends Binder { 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/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 d668c691ee0dfb719299e30d19d3da57a1070009..defaa20a84c79fe6374836191b517ed45962c5aa 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.animateFloatAsState 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.input.pointer.pointerInput 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 androidx.compose.ui.unit.times 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 @@ -195,6 +198,7 @@ private fun Bouncer( 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, @@ -221,21 +225,7 @@ private fun Bouncer( ) } - 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) { @@ -320,6 +310,37 @@ private fun UserInputArea( } } +/** + * 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( 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 17726ab06e0f921f9d3543a13d0c73f14952bf9c..142978284cfbbb3478aa30a0c58e830becf79145 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 @@ -16,15 +16,17 @@ 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 @@ -44,9 +46,11 @@ 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 @@ -54,8 +58,7 @@ 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), ) { @@ -65,31 +68,22 @@ fun CommunalHub( 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, - deleteOnClick = viewModel::onDeleteWidget - ) - } + 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) { @@ -101,15 +95,26 @@ fun CommunalHub( } } -// 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, ) { @@ -117,31 +122,70 @@ private fun ContentCard( Box( modifier = modifier.fillMaxSize().background(Color.White), ) { - // TODO(b/308148193): this will be cleaned up soon once the change to convert to - // CommunalContentUiModel interface is merged - val widgetId = getWidgetId(model.id) - widgetId?.let { - IconButton(onClick = { deleteOnClick(it) }) { - Icon( - Icons.Default.Close, - LocalContext.current.getString(R.string.button_to_remove_widget) - ) - } + IconButton(onClick = { deleteOnClick(model.appWidgetId) }) { + Icon( + Icons.Default.Close, + LocalContext.current.getString(R.string.button_to_remove_widget) + ) } 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 -> + 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 = { 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 = {}, + ) +} + private fun CommunalContentSize.dp(): Dp { return when (this) { CommunalContentSize.FULL -> Dimensions.CardHeightFull @@ -150,23 +194,6 @@ private fun CommunalContentSize.dp(): Dp { } } -private fun getWidgetId(id: String): Int? { - return if (id.startsWith("widget_")) id.substring("widget_".length).toInt() else null -} - -// 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/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 0000000000000000000000000000000000000000..c2117ae5bda40814b6f8814a1bc3d48091db3748 --- /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 0000000000000000000000000000000000000000..fde3ad723d718b3bbc7e8500025788afcd4c7c03 --- /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 0000000000000000000000000000000000000000..7196de6608cb0de4fef8b0293fe311f18c39ee73 --- /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 0000000000000000000000000000000000000000..9a3129f2e962aaea37ae13829337b1a4248a3bfe --- /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/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt similarity index 54% rename from packages/SystemUI/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt rename to packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt index 020903057eb3acc64045da80c4fd0a3a314e0751..262795fe0eb6de07dc0f4a5fd449fc9c0f04df3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt @@ -17,48 +17,62 @@ 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.Dispatchers -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.runBlocking +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.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(JUnit4::class) +@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() = - runBlocking(IMMEDIATE) { - var callState: Int? = null - val job = underTest.callState.onEach { callState = it }.launchIn(this) + testScope.runTest { + val callState by collectLastValue(underTest.callState) + runCurrent() + val listenerCaptor = kotlinArgumentCaptor<TelephonyCallback.CallStateListener>() verify(manager).addCallStateListener(listenerCaptor.capture()) val listener = listenerCaptor.value @@ -71,13 +85,25 @@ class TelephonyRepositoryImplTest : SysuiTestCase() { listener.onCallStateChanged(2) assertThat(callState).isEqualTo(2) + } - job.cancel() + @Test + fun isInCall() = + testScope.runTest { + val isInCall by collectLastValue(underTest.isInCall) + runCurrent() - verify(manager).removeCallStateListener(listener) - } + 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) - companion object { - private val IMMEDIATE = Dispatchers.Main.immediate - } + assertThat(isInCall).isTrue() + + whenever(telecomManager.isInCall).thenReturn(false) + listener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE) + + assertThat(isInCall).isFalse() + } } diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9a3c6d5b322f28b91acc5178ebcaadc9d7d23515..8780f58743cdce5e2b026aae8f6a50a389bd54c3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3266,9 +3266,4 @@ <string name="privacy_dialog_active_app_usage_2">In use by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string> <!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] --> <string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string> - - <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] --> - <string name="keyboard_backlight_dialog_title">Keyboard backlight</string> - <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] --> - <string name="keyboard_backlight_value">Level %1$d of %2$d</string> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java index 5de370f22eb8d98a5865344562a24f914eeb0927..77338410642c469fbc630ccb981ea5b776aa25ec 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.PowerManager; 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 @@ import javax.inject.Inject; /** 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 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> { @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 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> { } 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 class EmergencyButtonController extends ViewController<EmergencyButton> { 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 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> { @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 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> { mConfigurationController = configurationController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; mPowerManager = powerManager; mActivityTaskManager = activityTaskManager; mShadeController = shadeController; @@ -252,9 +251,9 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> { /** 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/systemui/bouncer/data/repository/EmergencyServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..bba00506df85533a3c8ac84a5e62d0b07a65ab49 --- /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 0000000000000000000000000000000000000000..f36ef6630a4802aaf4fadc8c40054b5f7e3e391e --- /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 0000000000000000000000000000000000000000..e398c930e86e52f815176e2c62396e04ef0dcac7 --- /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 0000000000000000000000000000000000000000..7f1730cb2abdd5b18ba86abb2865f06cbedd6798 --- /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 ef0609a99e0576c0f6d54382ab3e0f860168b89b..0f7772454b782eda29797a933f1a706fdc4b49c7 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 android.graphics.Bitmap 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 @@ class BouncerViewModel( 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 @@ class BouncerViewModel( ), ) - 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 @@ class BouncerViewModel( } } - /** 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 @@ object BouncerViewModelModule { bouncerInteractor: BouncerInteractor, authenticationInteractor: AuthenticationInteractor, flags: SceneContainerFlags, - telephonyInteractor: TelephonyInteractor, userSwitcherViewModel: UserSwitcherViewModel, + actionButtonInteractor: BouncerActionButtonInteractor, ): BouncerViewModel { return BouncerViewModel( applicationContext = applicationContext, @@ -281,10 +285,10 @@ object BouncerViewModelModule { 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 e4492743271961482362ce6381e416dc859d9fee..7fa762a6614fa5c9b0a04a5b394ecad12016c52f 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 @@ import kotlinx.coroutines.flow.stateIn 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 @@ constructor( 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 @@ constructor( } 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 c3421de8f6637f3c6e2914896e16ce3ee8a53e35..273adcf83271ff7f951a07024a105181391f82ed 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt @@ -17,6 +17,7 @@ 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 @@ -26,6 +27,7 @@ import dagger.Module includes = [ CommunalRepositoryModule::class, + CommunalMediaRepositoryModule::class, CommunalTutorialRepositoryModule::class, CommunalWidgetRepositoryModule::class, CommunalDatabaseModule::class, 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 0000000000000000000000000000000000000000..e41c32261c11cffd54bf3d32bfd0310c8581fefc --- /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 0000000000000000000000000000000000000000..2c6d9e4c39c3cfb66d2a2638ad7a685430dbd258 --- /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 b40570bccc55c08495092c445b5e4bedc053b00c..3119b9e98bac5ae3e872b61bc4c412f51fe2a86d 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 @@ -21,16 +21,24 @@ import com.android.systemui.communal.shared.model.CommunalSceneKey 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]. @@ -46,6 +54,8 @@ class CommunalRepositoryImpl @Inject constructor( private val featureFlagsClassic: FeatureFlagsClassic, + sceneContainerFlags: SceneContainerFlags, + sceneContainerRepository: SceneContainerRepository, ) : CommunalRepository { override val isCommunalEnabled: Boolean get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub() @@ -57,4 +67,11 @@ constructor( 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/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 2c683ee828aab06b468975460b01bb42d24efeb0..eb36b19972f98d0f2f717e8a169fe1c17a9ffc6c 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,25 +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, private val widgetRepository: CommunalWidgetRepository, + mediaRepository: CommunalMediaRepository, + smartspaceRepository: SmartspaceRepository, + tutorialInteractor: CommunalTutorialInteractor, + private val appWidgetHost: AppWidgetHost, ) { /** Whether communal features are enabled. */ @@ -44,14 +58,6 @@ constructor( /** A flow of info about the widget to be displayed, or null if widget is unavailable. */ 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]. @@ -76,4 +82,72 @@ constructor( /** 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 7f43eb5682d653ca69bc832c7f31a87dbf00969a..5ca89f28f1fc249ab0d37e242ae7cbc365d0a7b2 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 @@ constructor( @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 @@ constructor( 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 @@ constructor( 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 0000000000000000000000000000000000000000..bb9b4b5f522f468f485096472e26c3beb8e197e7 --- /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/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt deleted file mode 100644 index b60dc2a21699bc4f20496cda1a76bce565b7c436..0000000000000000000000000000000000000000 --- 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/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt index d8d1dc0c11ef757c220af776d7ed01413a0da86e..702554ab4943345a927d92bb0a7f1307ea2747b0 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt @@ -33,8 +33,8 @@ constructor( defaultCommunalWidgetSection: DefaultCommunalWidgetSection, ) : KeyguardBlueprint { override val id: String = COMMUNAL - override val sections: Set<KeyguardSection> = - setOf( + override val sections: List<KeyguardSection> = + listOf( defaultCommunalHubSection, defaultCommunalWidgetSection, ) 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 197dece47cc5cd45052ca3d5aac81cb3aa0bd7ad..5efe6ceeb65acf7a2d118e32e100c88e184dc8f2 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,53 +16,33 @@ package com.android.systemui.communal.ui.viewmodel -import android.appwidget.AppWidgetHost import android.content.ComponentName -import android.content.Context 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) diff --git a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java index b98794ef8026ea0c47cddd14f85c9f3ba8603e4d..9bb23d8564e42b282137b8e466ac0041e080c973 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 @@ public class SmartSpaceComplication implements Complication { 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 @@ public class SmartSpaceComplication implements Complication { 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 58dcf06916874ebb963786984ab09eda6551e92d..b34b4599cf70b54a3cefcbbe19f304689e322f3c 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.FingerprintReEnrollNotification; 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.user.domain.UserDomainLayerModule; 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 @@ import javax.inject.Named; AuthenticationModule.class, BiometricsModule.class, BiometricsDomainLayerModule.class, + BouncerInteractorModule.class, BouncerViewModule.class, ClipboardOverlayModule.class, ClockRegistryModule.class, @@ -199,6 +202,7 @@ import javax.inject.Named; 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 cf868856c4198d4de08ae77549e7b43370c09b53..20a9e5d572c9e0a59ea6eb599e4fa9f897e63ec3 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.VirtualDeviceManager 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 @@ constructor( private val virtualDeviceManager: VirtualDeviceManager, keyguardRepository: KeyguardRepository, displayRepository: DisplayRepository, + @Background backgroundCoroutineDispatcher: CoroutineDispatcher, ) : ConnectedDisplayInteractor { override val connectedDisplayState: Flow<State> = @@ -101,6 +105,7 @@ constructor( State.CONNECTED } } + .flowOn(backgroundCoroutineDispatcher) .distinctUntilChanged() override val connectedDisplayAddition: Flow<Unit> = @@ -108,6 +113,7 @@ constructor( .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/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index c9748f9546700c2b57d13a291db80d82e0622a2e..0e333f21dd14aece3c23a223103b3e4dcddcf005 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.flags.Flags; 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 @@ public class DreamOverlayStateController implements /** * 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 @@ public class DreamOverlayStateController implements 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 @@ public class DreamOverlayStateController implements private final Collection<Complication> mComplications = new HashSet(); private final FeatureFlags mFeatureFlags; + private final WeakReferenceFactory mWeakReferenceFactory; private final int mSupportedTypes; @@ -117,11 +125,13 @@ public class DreamOverlayStateController implements 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 @@ public class DreamOverlayStateController implements mExecutor.execute(() -> { if (mComplications.add(complication)) { mLogger.logAddComplication(complication.toString()); - mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged()); + notifyCallbacksLocked(Callback::onComplicationsChanged); } }); } @@ -160,7 +170,7 @@ public class DreamOverlayStateController implements mExecutor.execute(() -> { if (mComplications.remove(complication)) { mLogger.logRemoveComplication(complication.toString()); - mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged()); + notifyCallbacksLocked(Callback::onComplicationsChanged); } }); } @@ -199,22 +209,33 @@ public class DreamOverlayStateController implements } 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 class DreamOverlayStateController implements 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 @@ public class DreamOverlayStateController implements 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 @@ public class DreamOverlayStateController implements mExecutor.execute(() -> { mLogger.logAvailableComplicationTypes(types); mAvailableComplicationTypes = types; - mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged); + notifyCallbacksLocked(Callback::onAvailableComplicationTypesChanged); }); } @@ -393,7 +420,7 @@ public class DreamOverlayStateController implements mExecutor.execute(() -> { mLogger.logShouldShowComplications(shouldShowComplications); mShouldShowComplications = shouldShowComplications; - mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged); + notifyCallbacksLocked(Callback::onAvailableComplicationTypesChanged); }); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt index 1e9be09bc3f3677785587f9eaaf53db1b96f29b2..e16bb0bb848214c0f4e4c4c60d2621232f07b30e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt @@ -30,7 +30,6 @@ import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.view.Window import android.view.WindowManager -import android.view.accessibility.AccessibilityEvent import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout @@ -79,29 +78,23 @@ class KeyboardBacklightDialog( private lateinit var stepProperties: StepViewProperties @ColorInt - private val filledRectangleColor = - getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) + var filledRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) @ColorInt - private val emptyRectangleColor = + var emptyRectangleColor = getColorFromStyle(com.android.internal.R.attr.materialColorOutlineVariant) @ColorInt - private val backgroundColor = - getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright) + var backgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceBright) @ColorInt - private val defaultIconColor = - getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary) + var defaultIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnPrimary) @ColorInt - private val defaultIconBackgroundColor = + var defaultIconBackgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorPrimary) @ColorInt - private val dimmedIconColor = - getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface) + var dimmedIconColor = getColorFromStyle(com.android.internal.R.attr.materialColorOnSurface) @ColorInt - private val dimmedIconBackgroundColor = + var dimmedIconBackgroundColor = getColorFromStyle(com.android.internal.R.attr.materialColorSurfaceDim) - private val levelContentDescription = context.getString(R.string.keyboard_backlight_value) - init { currentLevel = initialCurrentLevel maxLevel = initialMaxLevel @@ -110,8 +103,6 @@ class KeyboardBacklightDialog( override fun onCreate(savedInstanceState: Bundle?) { setUpWindowProperties(this) setWindowPosition() - // title is used for a11y announcement - window?.setTitle(context.getString(R.string.keyboard_backlight_dialog_title)) updateResources() rootView = buildRootView() setContentView(rootView) @@ -168,12 +159,6 @@ class KeyboardBacklightDialog( currentLevel = current updateIconTile() updateStepColors() - updateAccessibilityInfo() - } - - private fun updateAccessibilityInfo() { - rootView.contentDescription = String.format(levelContentDescription, currentLevel, maxLevel) - rootView.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) } private fun updateIconTile() { 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 654f2d10620669875e268969be28ef2cc955356e..f5f5571dbb1b9d06217e84090bbf810c3dc68870 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 @@ import kotlinx.coroutines.flow.transformLatest /** * 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 @@ import kotlinx.coroutines.flow.transformLatest 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 @@ constructor( } } - 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 @@ constructor( .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 @@ constructor( 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/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt index f5ef27daecdd7aea2487961efdaee26e54545d6d..fbd62cef7dfbad641544a784825ad57823205fb7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt @@ -17,33 +17,29 @@ package com.android.systemui.keyguard.data.repository +import android.util.Log 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.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule import java.io.PrintWriter import java.util.TreeMap import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.launch /** * Manages blueprint changes for the lockscreen. * * To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in - * the dagger module: + * the dagger module: [KeyguardBlueprintModule] * * A Blueprint determines how the layout should be constrained on a high level. * * A Section is a modular piece of code that implements the constraints. The blueprint uses the * sections to define the constraints. - * - * @see KeyguardBlueprintModule */ @SysUISingleton class KeyguardBlueprintRepository @@ -51,18 +47,16 @@ class KeyguardBlueprintRepository constructor( configurationRepository: ConfigurationRepository, blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>, - @Application private val applicationScope: CoroutineScope, ) { private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> = TreeMap() private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1) val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow() + val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange + init { blueprintIdMap.putAll(blueprints.associateBy { it.id }) applyBlueprint(blueprintIdMap[DEFAULT]!!) - applicationScope.launch { - configurationRepository.onAnyConfigurationChange.collect { refreshBlueprint() } - } } /** @@ -86,9 +80,18 @@ constructor( * @return whether the transition has succeeded. */ fun applyBlueprint(blueprintId: String?): Boolean { - val blueprint = blueprintIdMap[blueprintId] ?: return false - applyBlueprint(blueprint) - return true + val blueprint = blueprintIdMap[blueprintId] + return if (blueprint != null) { + applyBlueprint(blueprint) + true + } else { + Log.e( + TAG, + "Could not find blueprint with id: $blueprintId. " + + "Perhaps it was not added to KeyguardBlueprintModule?" + ) + false + } } /** Emits the blueprint value to the collectors. */ @@ -107,4 +110,8 @@ constructor( fun printBlueprints(pw: PrintWriter) { blueprintIdMap.onEachIndexed { index, entry -> pw.println("$index: ${entry.key}") } } + + companion object { + private const val TAG = "KeyguardBlueprintRepository" + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt index 6ce91854ea56dd8fb5d4b55d853cd68e5cf9513e..7dab84dc7da33f248feb48249c7d2a318dade22c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt @@ -17,16 +17,56 @@ package com.android.systemui.keyguard.domain.interactor +import android.content.Context import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository +import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint +import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint +import com.android.systemui.statusbar.policy.SplitShadeStateController import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch @SysUISingleton class KeyguardBlueprintInteractor @Inject -constructor(private val keyguardBlueprintRepository: KeyguardBlueprintRepository) { +constructor( + private val keyguardBlueprintRepository: KeyguardBlueprintRepository, + @Application private val applicationScope: CoroutineScope, + private val context: Context, + private val splitShadeStateController: SplitShadeStateController, +) { + val blueprint = keyguardBlueprintRepository.blueprint + init { + applicationScope.launch { + keyguardBlueprintRepository.configurationChange + .onStart { emit(Unit) } + .collect { updateBlueprint() } + } + } + + /** + * Detects when a new blueprint should be applied and calls [transitionToBlueprint]. This may + * end up reapplying the same blueprint, which is fine as configuration may have changed. + */ + private fun updateBlueprint() { + val useSplitShade = + splitShadeStateController.shouldUseSplitNotificationShade(context.resources) + + val blueprintId = + if (useSplitShade) { + SplitShadeKeyguardBlueprint.ID + } else { + DefaultKeyguardBlueprint.DEFAULT + } + + transitionToBlueprint(blueprintId) + } + /** * Transitions to a blueprint. * diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt index 7fc1911a3477887f827a411cfd7a159ae1ae8f78..344044019c0dde7b445fd7212f28aa0084389f5f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt @@ -22,7 +22,7 @@ import androidx.constraintlayout.widget.ConstraintSet /** Determines the constraints for the ConstraintSet in the lockscreen root view. */ interface KeyguardBlueprint { val id: String - val sections: Set<KeyguardSection> + val sections: List<KeyguardSection> /** * Removes views of old blueprint and add views of new blueprint. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt index f1ea4469a2ae9f48988ca728005247f626cca6f8..2e64c41bace817d3d44adde61a676ca60f055902 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt @@ -32,7 +32,6 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSec import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection -import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines import javax.inject.Inject /** @@ -53,7 +52,6 @@ constructor( defaultStatusViewSection: DefaultStatusViewSection, defaultStatusBarSection: DefaultStatusBarSection, defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection, - splitShadeGuidelines: SplitShadeGuidelines, aodNotificationIconsSection: AodNotificationIconsSection, aodBurnInSection: AodBurnInSection, communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, @@ -63,7 +61,7 @@ constructor( override val id: String = DEFAULT override val sections = - setOf( + listOf( defaultIndicationAreaSection, defaultDeviceEntryIconSection, defaultShortcutsSection, @@ -72,7 +70,6 @@ constructor( defaultStatusViewSection, defaultStatusBarSection, defaultNotificationStackScrollLayoutSection, - splitShadeGuidelines, aodNotificationIconsSection, aodBurnInSection, communalTutorialIndicatorSection, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt index fda4c3d5376a6188ac00c652868054d8af1f23f4..4f1a754adbd59983228bdbb8236e5096a6435e3f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt @@ -31,6 +31,12 @@ abstract class KeyguardBlueprintModule { defaultLockscreenBlueprint: DefaultKeyguardBlueprint ): KeyguardBlueprint + @Binds + @IntoSet + abstract fun bindSplitShadeBlueprint( + splitShadeBlueprint: SplitShadeKeyguardBlueprint + ): KeyguardBlueprint + @Binds @IntoSet abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt index f8dd7c1a58c7e3ee7ddaa89039e4a45236b305f6..d8b368b4a0d38bd59cfa3a05926dfffaaf511469 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt @@ -52,7 +52,7 @@ constructor( override val id: String = SHORTCUTS_BESIDE_UDFPS override val sections = - setOf( + listOf( defaultIndicationAreaSection, defaultDeviceEntryIconSection, defaultAmbientIndicationAreaSection, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt new file mode 100644 index 0000000000000000000000000000000000000000..35679b84771b4dad07933fe4ed70d2c98b0565e1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt @@ -0,0 +1,79 @@ +/* + * 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.layout.blueprints + +import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.KeyguardBlueprint +import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection +import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection +import javax.inject.Inject + +/** + * Split-shade layout, mostly used for larger devices like foldables and tablets when in landscape + * orientation. + */ +@SysUISingleton +@JvmSuppressWildcards +class SplitShadeKeyguardBlueprint +@Inject +constructor( + defaultIndicationAreaSection: DefaultIndicationAreaSection, + defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection, + defaultShortcutsSection: DefaultShortcutsSection, + defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection, + defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection, + defaultStatusViewSection: DefaultStatusViewSection, + defaultStatusBarSection: DefaultStatusBarSection, + splitShadeNotificationStackScrollLayoutSection: SplitShadeNotificationStackScrollLayoutSection, + splitShadeGuidelines: SplitShadeGuidelines, + aodNotificationIconsSection: AodNotificationIconsSection, + aodBurnInSection: AodBurnInSection, + communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, +) : KeyguardBlueprint { + override val id: String = ID + + override val sections = + listOf( + defaultIndicationAreaSection, + defaultDeviceEntryIconSection, + defaultShortcutsSection, + defaultAmbientIndicationAreaSection, + defaultSettingsPopupMenuSection, + defaultStatusViewSection, + defaultStatusBarSection, + splitShadeNotificationStackScrollLayoutSection, + splitShadeGuidelines, + aodNotificationIconsSection, + aodBurnInSection, + communalTutorialIndicatorSection, + ) + + companion object { + const val ID = "split-shade" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt index 199566129eb3f2924a8187284e2c1f76b7032aa2..078fefff394a561fec3f45ddbd95cb7814589831 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt @@ -18,9 +18,6 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM import androidx.constraintlayout.widget.ConstraintSet.END @@ -29,55 +26,34 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP 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.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer -import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel import javax.inject.Inject +/** Single column format for notifications (default for phones) */ class DefaultNotificationStackScrollLayoutSection @Inject constructor( - private val context: Context, - private val featureFlags: FeatureFlags, - private val notificationPanelView: NotificationPanelView, - private val sharedNotificationContainer: SharedNotificationContainer, - private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, - private val controller: NotificationStackScrollLayoutController, - private val smartspaceViewModel: KeyguardSmartspaceViewModel -) : KeyguardSection() { - private val placeHolderId = R.id.nssl_placeholder - - override fun addViews(constraintLayout: ConstraintLayout) { - if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { - return - } - // This moves the existing NSSL view to a different parent, as the controller is a - // singleton and recreating it has other bad side effects - notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let { - (it.parent as ViewGroup).removeView(it) - sharedNotificationContainer.addNotificationStackScrollLayout(it) - } - - val view = View(context, null).apply { id = placeHolderId } - constraintLayout.addView(view) - } - - override fun bindData(constraintLayout: ConstraintLayout) { - if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { - return - } - SharedNotificationContainerBinder.bind( - sharedNotificationContainer, - sharedNotificationContainerViewModel, - controller, - ) - } - + context: Context, + featureFlags: FeatureFlags, + notificationPanelView: NotificationPanelView, + sharedNotificationContainer: SharedNotificationContainer, + sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, + controller: NotificationStackScrollLayoutController, + private val smartspaceViewModel: KeyguardSmartspaceViewModel, +) : + NotificationStackScrollLayoutSection( + context, + featureFlags, + notificationPanelView, + sharedNotificationContainer, + sharedNotificationContainerViewModel, + controller, + ) { override fun applyConstraints(constraintSet: ConstraintSet) { if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { return @@ -85,41 +61,29 @@ constructor( constraintSet.apply { val bottomMargin = context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin) - val useSplitShade = - context.resources.getBoolean(R.bool.config_use_split_notification_shade) - val topAlignment = - if (useSplitShade) { - TOP - } else { - BOTTOM - } if (featureFlags.isEnabled(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT)) { connect( R.id.nssl_placeholder, TOP, smartspaceViewModel.smartspaceViewId, - topAlignment, + BOTTOM, bottomMargin ) setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin) } else { - connect( - R.id.nssl_placeholder, - TOP, - R.id.keyguard_status_view, - topAlignment, - bottomMargin - ) + connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, BOTTOM, bottomMargin) } - connect(R.id.nssl_placeholder, START, PARENT_ID, START) connect(R.id.nssl_placeholder, END, PARENT_ID, END) - connect(R.id.nssl_placeholder, BOTTOM, R.id.lock_icon_view, TOP) - } - } - override fun removeViews(constraintLayout: ConstraintLayout) { - constraintLayout.removeView(placeHolderId) + val lockId = + if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + R.id.device_entry_icon_view + } else { + R.id.lock_icon_view + } + connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt new file mode 100644 index 0000000000000000000000000000000000000000..00966f235a5789dd783ca9667f8edb0b811896b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt @@ -0,0 +1,79 @@ +/* + * 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.layout.sections + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.constraintlayout.widget.ConstraintLayout +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.shared.model.KeyguardSection +import com.android.systemui.res.R +import com.android.systemui.shade.NotificationPanelView +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer +import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel +import kotlinx.coroutines.DisposableHandle + +abstract class NotificationStackScrollLayoutSection +constructor( + protected val context: Context, + protected val featureFlags: FeatureFlags, + private val notificationPanelView: NotificationPanelView, + private val sharedNotificationContainer: SharedNotificationContainer, + private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, + private val controller: NotificationStackScrollLayoutController, +) : KeyguardSection() { + private val placeHolderId = R.id.nssl_placeholder + private var disposableHandle: DisposableHandle? = null + + override fun addViews(constraintLayout: ConstraintLayout) { + if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { + return + } + // This moves the existing NSSL view to a different parent, as the controller is a + // singleton and recreating it has other bad side effects + notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let { + (it.parent as ViewGroup).removeView(it) + sharedNotificationContainer.addNotificationStackScrollLayout(it) + } + + val view = View(context, null).apply { id = placeHolderId } + constraintLayout.addView(view) + } + + override fun bindData(constraintLayout: ConstraintLayout) { + if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { + return + } + disposableHandle?.dispose() + disposableHandle = + SharedNotificationContainerBinder.bind( + sharedNotificationContainer, + sharedNotificationContainerViewModel, + controller, + ) + } + + override fun removeViews(constraintLayout: ConstraintLayout) { + disposableHandle?.dispose() + constraintLayout.removeView(placeHolderId) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt new file mode 100644 index 0000000000000000000000000000000000000000..bf95c77229e9660109e46b642f6f2bbef2d6a41a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.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.ui.view.layout.sections + +import android.content.Context +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel +import com.android.systemui.res.R +import com.android.systemui.shade.NotificationPanelView +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer +import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel +import javax.inject.Inject + +/** Large-screen format for notifications, shown as two columns on the device */ +class SplitShadeNotificationStackScrollLayoutSection +@Inject +constructor( + context: Context, + featureFlags: FeatureFlags, + notificationPanelView: NotificationPanelView, + sharedNotificationContainer: SharedNotificationContainer, + sharedNotificationContainerViewModel: SharedNotificationContainerViewModel, + controller: NotificationStackScrollLayoutController, + private val smartspaceViewModel: KeyguardSmartspaceViewModel, +) : + NotificationStackScrollLayoutSection( + context, + featureFlags, + notificationPanelView, + sharedNotificationContainer, + sharedNotificationContainerViewModel, + controller, + ) { + override fun applyConstraints(constraintSet: ConstraintSet) { + if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) { + return + } + constraintSet.apply { + val bottomMargin = + context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin) + + if (featureFlags.isEnabled(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT)) { + connect( + R.id.nssl_placeholder, + TOP, + smartspaceViewModel.smartspaceViewId, + TOP, + bottomMargin + ) + setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin) + } else { + connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, TOP, bottomMargin) + } + connect(R.id.nssl_placeholder, START, PARENT_ID, START) + connect(R.id.nssl_placeholder, END, PARENT_ID, END) + + val lockId = + if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) { + R.id.device_entry_icon_view + } else { + R.id.lock_icon_view + } + connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP) + } + } +} 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 83a6e58cc44479e335bfcaaf5214924e478a221e..773c292befcf36a0d99d5a0e2d2b6fafd3d7a508 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.net.Uri 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.notification.stack.MediaContainerView 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 @@ constructor( 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 @@ constructor( } 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 @@ constructor( 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 eea369f15a16fd646b6da41fafd314919e5e54bc..654fffe8947126d1dcce3dcee0db36d86b513147 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.ArrayAdapter 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 @@ open class BaseScreenSharePermissionDialog( 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 @@ open class BaseScreenSharePermissionDialog( /** 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 @@ open class BaseScreenSharePermissionDialog( 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 eacfa578ac44d8d9635d1c5f76f2723805916edb..039372d878358ce3ad3e5bea291f3b90a7425f37 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.appselector.MediaProjectionAppSelect 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 @@ public class MediaProjectionPermissionActivity extends Activity // 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 89% 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 cff22b0dc019c6301f159e909b0111833f8531b0..8453af19d26fb1506ac3da87732922254b0c9b40 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 @@ class MediaProjectionPermissionDialog( 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/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index c77f3f49a77c98ae2587aca02fec555bb2a45d16..fa03dc245745e17cb774aa1727b1f70455da09e4 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.Paint 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.core.view.isVisible 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_ENTRY_TO_ACTIVE_VELOCITY = 4.5f 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 @@ internal constructor( 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 @@ internal constructor( 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 @@ internal constructor( vibratorHelper, configurationController, latencyTracker, - featureFlags ) backPanelController.init() return backPanelController @@ -992,35 +980,22 @@ internal constructor( 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/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt index 934f31040ce1dd895f152421c6b0b247e5507368..82420875e0de77d10b0d4d1ea1ae388fd8f688a1 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.WakeSleepReason 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 @@ interface PowerRepository { /** 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 @@ constructor( 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 @@ constructor( ) } - 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/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index 05f125f6f531cff2c30939ff383174765e0d1cf2..bff0b93dccb05960c3841ae67fd23423472182d7 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.mediaprojection.devicepolicy.ScreenCaptureDisabledDi 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 @@ public class RecordingController 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 @@ public class RecordingController UserContextProvider userContextProvider, Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver, UserTracker userTracker, - MediaProjectionMetricsLogger mediaProjectionMetricsLogger) { + MediaProjectionMetricsLogger mediaProjectionMetricsLogger, + SystemUIDialog.Factory dialogFactory) { mMainExecutor = mainExecutor; mContext = context; mFlags = flags; @@ -129,6 +132,7 @@ public class RecordingController mUserContextProvider = userContextProvider; mUserTracker = userTracker; mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger; + mDialogFactory = dialogFactory; BroadcastOptions options = BroadcastOptions.makeBasic(); options.setInteractive(true); @@ -166,15 +170,14 @@ public class RecordingController 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 f74234bc2e21998d9bcab7cfcfa95dfd8c24b236..e57a0fddc936459c8c04bf7c50a61331c5644134 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 @@ package com.android.systemui.screenrecord 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 androidx.annotation.LayoutRes 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 @@ class ScreenRecordPermissionDialog( private val onStartRecordingClicked: Runnable?, mediaProjectionMetricsLogger: MediaProjectionMetricsLogger, ) : - BaseScreenSharePermissionDialog( - context, + BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>( createOptionList(), appName = null, hostUid = hostUid, @@ -68,10 +66,10 @@ class ScreenRecordPermissionDialog( 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 @@ class ScreenRecordPermissionDialog( 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 @@ class ScreenRecordPermissionDialog( 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/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt index 03be88fc31d9a53dfd1ce1944cb7142176623f22..c59ef2632f159fd40575774ffcc6cdb22ee22761 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 @@ package com.android.systemui.smartspace.dagger 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 0000000000000000000000000000000000000000..2fc0ec290a90452a53bc7fe42403e6d316436e51 --- /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 0000000000000000000000000000000000000000..c77bcc50b69a8455cc9daec3a80c01815b18e891 --- /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 65b798a14e0b25121cefd19f4e3af38e548a9c88..62c3e9e486b43e94828b3f5968d3ec5225b6a807 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 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder 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.StatusBarIconView 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.stopAnimating 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 @@ object NotificationIconContainerViewBinder { 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 @@ object NotificationIconContainerViewBinder { 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 @@ object NotificationIconContainerViewBinder { 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 @@ object NotificationIconContainerViewBinder { } } - /** 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 @@ object NotificationIconContainerViewBinder { } } + val iconBindings = mutableMapOf<String, Job>() var prevIcons = NotificationIconsViewData() sample(layoutParams, ::Pair).collect { (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams), @@ -261,15 +248,20 @@ object NotificationIconContainerViewBinder { } 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 @@ object NotificationIconContainerViewBinder { // 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/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index 6785da4bf4f15710c4d430e287606195dda87903..a1a0ccac35001623ff4480a8dd582901a307a240 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -22,6 +22,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel +import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.launch /** Binds the shared notification container to its view-model. */ @@ -32,39 +33,46 @@ object SharedNotificationContainerBinder { view: SharedNotificationContainer, viewModel: SharedNotificationContainerViewModel, controller: NotificationStackScrollLayoutController, - ) { - view.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.CREATED) { - launch { - viewModel.configurationBasedDimensions.collect { - view.updateConstraints( - useSplitShade = it.useSplitShade, - marginStart = it.marginStart, - marginTop = it.marginTop, - marginEnd = it.marginEnd, - marginBottom = it.marginBottom, - ) + ): DisposableHandle { + val disposableHandle = + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.CREATED) { + launch { + viewModel.configurationBasedDimensions.collect { + view.updateConstraints( + useSplitShade = it.useSplitShade, + marginStart = it.marginStart, + marginTop = it.marginTop, + marginEnd = it.marginEnd, + marginBottom = it.marginBottom, + ) - controller.setOverExpansion(0f) - controller.setOverScrollAmount(0) - controller.updateFooter() + controller.setOverExpansion(0f) + controller.setOverScrollAmount(0) + controller.updateFooter() + } } - } - launch { - viewModel.maxNotifications.collect { - controller.setMaxDisplayedNotifications(it) + launch { + viewModel.maxNotifications.collect { + controller.setMaxDisplayedNotifications(it) + } } - } - launch { - viewModel.position.collect { - val animate = it.animate || controller.isAddOrRemoveAnimationPending() - controller.updateTopPadding(it.top, animate) + launch { + viewModel.position.collect { + val animate = it.animate || controller.isAddOrRemoveAnimationPending() + controller.updateTopPadding(it.top, animate) + } } + + launch { viewModel.translationY.collect { controller.setTranslationY(it) } } } + } - launch { viewModel.translationY.collect { controller.setTranslationY(it) } } + return object : DisposableHandle { + override fun dispose() { + disposableHandle.dispose() } } } 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 13c99647f00bfc058f8e4fd596863d369d91cc2e..9471574fc755cc702d9dc9b803315bb7834151fb 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 @@ -99,6 +99,17 @@ interface MobileConnectionsRepository { */ 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 87dd17ec486534bcff87c9c687e3eb95a95d2124..8a8e33efbcef94e1918deb2d68fe32401ad64164 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 @@ constructor( 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 2ecd435177ee5332d053ed7c01af50c4a4c0a10f..2b3c6326032c35d455740d2bc35e13ad54991614 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 @@ constructor( 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 cf7bf86504fb65d1af24045669d518c583669362..2a510e41ec9c9e6a1b13c640d72414a5d1e19883 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 @@ constructor( 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 @@ constructor( ) .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 3b300249aac20b70a0cafa5f30fd16382c7cf1ec..b1b6014bfbdebad8ec1122044016db2049477e1a 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 @@ interface TelephonyRepository { 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 4642f552615a07cd92ede5de3afd9c7c6ae112a7..4b0e5d188ffaf1de71270de976b3127fe920c1d3 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.dagger.SysUISingleton 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/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt index 78fb7a491534948540506c3a32b8afe49ab421c7..3ed05aac3e10d7f9838e85cab7b4f534bd0f93b6 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 @@ constructor( @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 0000000000000000000000000000000000000000..e7a91e505e47cf8c52077b400594704eba3872a5 --- /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 0000000000000000000000000000000000000000..658f0404aa84c441d38b5165845a2b3f7ade6fcb --- /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/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt index c61b11ae73363566bc5c40c157d0b67c66dfc4bb..9a95b17fc7d12b632391e46467f0d74d1fb28b41 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 @@ import org.mockito.MockitoAnnotations @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 @@ class EmergencyButtonControllerTest : SysuiTestCase() { @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 @@ class EmergencyButtonControllerTest : SysuiTestCase() { emergencyButton, configurationController, keyguardUpdateMonitor, - telephonyManager, powerManager, activityTaskManager, shadeController, 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 a0491918535063de968570f4c3786c868da390eb..44c57f34fa1b6a62037bc1b49663e8e7a699d668 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 @@ package com.android.systemui.bouncer.data.repository 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.Mock 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 2c97809bf3672985777977d80541a76086d7aee0..c159b66c10a0c914b5f16de945bbf5a921d044de 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 @@ class BouncerViewModelTest : SysuiTestCase() { 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 @@ class BouncerViewModelTest : SysuiTestCase() { 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 b75f3e0876ff69655d9899bbf7be80385d3d92c9..2cc8f0a479550444d47005a62abeca6eebfb162d 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 @@ import org.mockito.MockitoAnnotations @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 @@ class KeyguardBouncerViewModelTest : SysuiTestCase() { @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 ba8dcef683c74905d706079f1578c9b6bea051d9..390742031381f7ecb56165536ae0ee66da61bddb 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 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { 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 bfaa6edefdca57bdddc3f59c0e5380f67d78ab4d..47db4f8faeca041e0521571cda62515386a723fb 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 @@ class PatternBouncerViewModelTest : SysuiTestCase() { 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 7873899f2d357582f59b9c1fda8340bab4ee2369..3ddac7e1ad1dfbc97edcb9ff187dbc7a1e1fbe0a 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 @@ class PinBouncerViewModelTest : SysuiTestCase() { 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/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..455f9865edf348c7ce308525145dce0e5b4a2856 --- /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/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 2f17b6fa35edd2330366538e85554668ecc78a5d..08d54c001d11e23b973299b7716e07421a56fa78 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.Before 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 @@ class CommunalInteractorTest : SysuiTestCase() { 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 @@ class CommunalInteractorTest : SysuiTestCase() { 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() + } + + @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 interactor = CommunalInteractor(communalRepository, widgetRepository) - assertThat(interactor.isCommunalEnabled).isFalse() + 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 @@ class CommunalInteractorTest : SysuiTestCase() { @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 @@ class CommunalInteractorTest : SysuiTestCase() { @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 61d1502f307e0403a8d5e9fcae757c1e29a99837..0000000000000000000000000000000000000000 --- 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 40f0ed3626db8717db34f64c86bfee28e0deedce..288f3b651a3cff2739a9928b64c3e4fc817e9218 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.FakeFeatureFlags; 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 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase { 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 0db3de2ce0dd30fe133a646c481726013461a799..1f18705edfdb0bd4643aa400f6188e16a8c6e5c2 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 @@ class ConnectedDisplayInteractorTest : SysuiTestCase() { ConnectedDisplayInteractorImpl( virtualDeviceManager, fakeKeyguardRepository, - fakeDisplayRepository + fakeDisplayRepository, + UnconfinedTestDispatcher(), ) private val testScope = TestScope(UnconfinedTestDispatcher()) 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 365f67b5e56607ad3e99e59733d1075ea7793571..6d5cd49b8af63c5d4e25a2a66e29ef1ce9a41178 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.flags.Flags; 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 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + final FakeWeakReferenceFactory mWeakReferenceFactory = new FakeWeakReferenceFactory(); + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -407,12 +410,36 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { 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/keyboard/backlight/ui/view/KeyboardBacklightDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialogTest.kt deleted file mode 100644 index 8b572eb3d906d99e8064095dbcb33f126b599916..0000000000000000000000000000000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialogTest.kt +++ /dev/null @@ -1,84 +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.keyboard.backlight.ui.view - -import android.testing.TestableLooper.RunWithLooper -import android.view.View -import android.view.accessibility.AccessibilityEvent -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.res.R -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@RunWithLooper -@SmallTest -@RunWith(JUnit4::class) -class KeyboardBacklightDialogTest : SysuiTestCase() { - - private lateinit var dialog: KeyboardBacklightDialog - private lateinit var rootView: View - private val descriptionString = context.getString(R.string.keyboard_backlight_value) - - @Before - fun setUp() { - dialog = - KeyboardBacklightDialog(context, initialCurrentLevel = 0, initialMaxLevel = MAX_LEVEL) - dialog.show() - rootView = dialog.requireViewById(R.id.keyboard_backlight_dialog_container) - } - - @Test - fun rootViewContentDescription_containsInitialLevel() { - assertThat(rootView.contentDescription).isEqualTo(contentDescriptionForLevel(INITIAL_LEVEL)) - } - - @Test - fun contentDescriptionUpdated_afterEveryLevelUpdate() { - val events = startCollectingAccessibilityEvents(rootView) - - dialog.updateState(current = 1, max = MAX_LEVEL) - - assertThat(rootView.contentDescription).isEqualTo(contentDescriptionForLevel(1)) - assertThat(events).contains(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION) - } - - private fun contentDescriptionForLevel(level: Int): String { - return String.format(descriptionString, level, MAX_LEVEL) - } - - private fun startCollectingAccessibilityEvents(rootView: View): MutableList<Int> { - val events = mutableListOf<Int>() - rootView.accessibilityDelegate = - object : View.AccessibilityDelegate() { - override fun sendAccessibilityEvent(host: View, eventType: Int) { - super.sendAccessibilityEvent(host, eventType) - events.add(eventType) - } - } - return events - } - - companion object { - private const val MAX_LEVEL = 5 - private const val INITIAL_LEVEL = 0 - } -} 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 90fd6523ec126b4707cdf9bf1bd95b6a31849036..4587ea6dbdc8fc321f53fa338cbde91ccc0be7f6 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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun fingerprintEnrollmentChange() = testScope.runTest { createBiometricSettingsRepository() + biometricsAreEnabledBySettings() val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() @@ -169,12 +170,35 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(fingerprintAllowed()).isFalse() } + @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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { 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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { faceAuthIsNonStrongBiometric() faceAuthIsEnrolled() doNotDisableKeyguardAuthFeatures() + biometricsAreEnabledBySettings() val convenienceBiometricAllowed = collectLastValue(underTest.isFaceAuthCurrentlyAllowed) runCurrent() @@ -258,7 +283,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { faceAuthIsEnrolled() createBiometricSettingsRepository() doNotDisableKeyguardAuthFeatures() - faceAuthIsEnabledByBiometricManager() + biometricsAreEnabledBySettings() runCurrent() val convenienceBiometricAllowed by @@ -291,6 +316,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testScope.runTest { fingerprintIsEnrolled(PRIMARY_USER_ID) createBiometricSettingsRepository() + biometricsAreEnabledBySettings() val fingerprintEnabledByDevicePolicy = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) @@ -316,7 +342,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { createBiometricSettingsRepository() val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - faceAuthIsEnabledByBiometricManager() + biometricsAreEnabledBySettings() doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) @@ -351,12 +377,18 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { 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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { faceAuthIsEnrolled() createBiometricSettingsRepository() - faceAuthIsEnabledByBiometricManager() + biometricsAreEnabledBySettings() doNotDisableKeyguardAuthFeatures() mobileConnectionsRepository.isAnySimSecure.value = false runCurrent() @@ -454,7 +486,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { deviceIsInPostureThatSupportsFaceAuth() doNotDisableKeyguardAuthFeatures() faceAuthIsStrongBiometric() - faceAuthIsEnabledByBiometricManager() + biometricsAreEnabledBySettings() mobileConnectionsRepository.isAnySimSecure.value = false onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) @@ -636,7 +668,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { deviceIsInPostureThatSupportsFaceAuth() doNotDisableKeyguardAuthFeatures() faceAuthIsStrongBiometric() - faceAuthIsEnabledByBiometricManager() + biometricsAreEnabledBySettings() onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(false, PRIMARY_USER_ID) @@ -660,7 +692,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { deviceIsInPostureThatSupportsFaceAuth() doNotDisableKeyguardAuthFeatures() faceAuthIsNonStrongBiometric() - faceAuthIsEnabledByBiometricManager() + biometricsAreEnabledBySettings() onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(false, PRIMARY_USER_ID) @@ -682,6 +714,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() = testScope.runTest { createBiometricSettingsRepository() + biometricsAreEnabledBySettings() val isFingerprintCurrentlyAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) @@ -723,6 +756,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { 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/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt index 66ead14d3d4c4a0a63fac3af64a1213f47e652f7..5852bdb5c35165e068a8f479ae7961ac140c512e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt @@ -25,10 +25,8 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBl import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -43,20 +41,16 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() { private lateinit var underTest: KeyguardBlueprintRepository @Mock lateinit var configurationRepository: ConfigurationRepository @Mock lateinit var defaultLockscreenBlueprint: DefaultKeyguardBlueprint - private val testDispatcher = StandardTestDispatcher() - private val testScope = TestScope(testDispatcher) - private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1) + private val testScope = TestScope(StandardTestDispatcher()) @Before fun setup() { MockitoAnnotations.initMocks(this) whenever(defaultLockscreenBlueprint.id).thenReturn(DEFAULT) - whenever(configurationRepository.onAnyConfigurationChange).thenReturn(configurationFlow) underTest = KeyguardBlueprintRepository( configurationRepository, setOf(defaultLockscreenBlueprint), - testScope.backgroundScope, ) } @@ -64,20 +58,7 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() { fun testApplyBlueprint_DefaultLayout() { testScope.runTest { val blueprint by collectLastValue(underTest.blueprint) - runCurrent() underTest.applyBlueprint(defaultLockscreenBlueprint) - runCurrent() - assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint) - } - } - - @Test - fun testConfigurationChange() { - testScope.runTest { - val blueprint by collectLastValue(underTest.blueprint) - runCurrent() - configurationFlow.tryEmit(Unit) - runCurrent() assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint) } } @@ -86,9 +67,7 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() { fun testRefreshBlueprint() { testScope.runTest { val blueprint by collectLastValue(underTest.blueprint) - runCurrent() underTest.refreshBlueprint() - runCurrent() assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt index b8a2e9d4afc7551b7fb19d5ed8356acd85a953cb..9fe40d73fa1d580a561064498b03bea77ade4441 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt @@ -21,24 +21,77 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository +import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint +import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint +import com.android.systemui.statusbar.policy.SplitShadeStateController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class KeyguardBlueprintInteractorTest : SysuiTestCase() { + private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1) private lateinit var underTest: KeyguardBlueprintInteractor + private lateinit var testScope: TestScope + @Mock private lateinit var splitShadeStateController: SplitShadeStateController @Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository @Before fun setup() { MockitoAnnotations.initMocks(this) - underTest = KeyguardBlueprintInteractor(keyguardBlueprintRepository) + testScope = TestScope(StandardTestDispatcher()) + whenever(keyguardBlueprintRepository.configurationChange).thenReturn(configurationFlow) + + underTest = + KeyguardBlueprintInteractor( + keyguardBlueprintRepository, + testScope.backgroundScope, + mContext, + splitShadeStateController, + ) + } + + @Test + fun testAppliesDefaultBlueprint() { + testScope.runTest { + whenever(splitShadeStateController.shouldUseSplitNotificationShade(any())) + .thenReturn(false) + + reset(keyguardBlueprintRepository) + configurationFlow.tryEmit(Unit) + runCurrent() + + verify(keyguardBlueprintRepository) + .applyBlueprint(DefaultKeyguardBlueprint.Companion.DEFAULT) + } + } + + @Test + fun testAppliesSplitShadeBlueprint() { + testScope.runTest { + whenever(splitShadeStateController.shouldUseSplitNotificationShade(any())) + .thenReturn(true) + + reset(keyguardBlueprintRepository) + configurationFlow.tryEmit(Unit) + runCurrent() + + verify(keyguardBlueprintRepository) + .applyBlueprint(SplitShadeKeyguardBlueprint.Companion.ID) + } } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt index 2831053184ccd65b147496d98450ed03035132e8..43d70adf26b0f73936897e5d83f2e9c9ae40ce06 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt @@ -86,7 +86,6 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() { defaultStatusViewSection, defaultStatusBarViewSection, defaultNSSLSection, - splitShadeGuidelines, aodNotificationIconsSection, aodBurnInSection, communalTutorialIndicatorSection, 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 7ad2ce8ae110a0aa42e5ce08973bf902cda09caf..f4293f035cd1f79f522ea9093da5b2cbafeada8c 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.GONE 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.phone.KeyguardBypassController 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 @@ class KeyguardMediaControllerTest : SysuiTestCase() { 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 5bfe56931bb42706e209db93296d44409e216eaf..a2eb5ef9e4639348c267b63d9af272123f15dacc 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 androidx.test.filters.SmallTest 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 @@ class MediaHierarchyManagerTest : SysuiTestCase() { 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/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt index 2d3dc585ac70d21611ce675b38ab7bc59c2c7312..f93d52b2c35ca33f30e7928076cf4a07d584af46 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 android.view.WindowManager 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 @@ class BackPanelControllerTest : SysuiTestCase() { @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 @@ class BackPanelControllerTest : SysuiTestCase() { vibratorHelper, configurationController, latencyTracker, - featureFlags ) mBackPanelController.setLayoutParams(layoutParams) mBackPanelController.setBackCallback(backCallback) @@ -106,32 +102,6 @@ class BackPanelControllerTest : SysuiTestCase() { @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 @@ class BackPanelControllerTest : SysuiTestCase() { 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 @@ class BackPanelControllerTest : SysuiTestCase() { @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 f566efe6f176a8093b295e8686963033ef794e77..f3b114d662b5dbf1f68fd8b2a14a7fd1ef901154 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 @@ class PowerRepositoryImplTest : SysuiTestCase() { 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/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 3feb5bfd088a52d7bb8e11e69071690a17af9a44..e84d274b4763895054b14df78fd984183499d78c 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.domain.interactor.FakeMobi 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 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.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations /** * Integration test cases for the Scene Framework. @@ -87,6 +99,10 @@ import org.junit.runner.RunWith @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 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { 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 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { ) 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 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { FakeMobileConnectionsRepository(), ), constants = mock(), - flags, + utils.featureFlags, scope = testScope.backgroundScope, ) @@ -173,6 +187,39 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { @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 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { 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 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { 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/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java index 49049bc51460b4a4c9deaa65d83cfb5ea9fe11ce..1b4ba6433ded413e75a116ab0ecbc77dc4fdd421 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.content.Intent; 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 @@ public class RecordingControllerTest extends SysuiTestCase { private FakeFeatureFlags mFeatureFlags; private RecordingController mController; + private TestSystemUIDialogFactory mDialogFactory; private static final int USER_ID = 10; @@ -103,6 +111,15 @@ public class RecordingControllerTest extends SysuiTestCase { 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 @@ public class RecordingControllerTest extends SysuiTestCase { mUserContextProvider, () -> mDevicePolicyResolver, mUserTracker, - mMediaProjectionMetricsLogger); + mMediaProjectionMetricsLogger, + mDialogFactory); mController.addCallback(mCallback); } @@ -218,10 +236,17 @@ public class RecordingControllerTest extends SysuiTestCase { 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); - - assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class); + Dialog dialog = + mController.createScreenRecordDialog( + mContext, + mFeatureFlags, + mDialogLaunchAnimator, + mActivityStarter, + /* onStartRecordingClicked= */ null); + + assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog); + assertThat(mDialogFactory.mLastDelegate) + .isInstanceOf(ScreenRecordPermissionDialogDelegate.class); } @Test @@ -253,10 +278,17 @@ public class RecordingControllerTest extends SysuiTestCase { 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); - - assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class); + Dialog dialog = + mController.createScreenRecordDialog( + mContext, + mFeatureFlags, + mDialogLaunchAnimator, + mActivityStarter, + /* onStartRecordingClicked= */ null); + + assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog); + assertThat(mDialogFactory.mLastDelegate) + .isInstanceOf(ScreenRecordPermissionDialogDelegate.class); } @Test @@ -273,4 +305,34 @@ public class RecordingControllerTest extends SysuiTestCase { /* 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 81% 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 fd381392c3e93d6f3f2a70df575f2afb737b7758..c848287aa53e5c56e290f52f9651d84b34c0aa01 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.testing.TestableLooper 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 @@ import org.mockito.MockitoAnnotations @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 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @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 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { 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 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @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 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @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 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @Test fun startClicked_singleAppSelected_passesHostUidToAppSelector() { - dialog.show() + showDialog() onSpinnerItemSelected(SINGLE_APP) clickOnStart() @@ -128,14 +143,14 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @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 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @Test fun showDialog_cancelClicked_dialogIsDismissed() { - dialog.show() + showDialog() clickOnCancel() @@ -153,7 +168,7 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @Test fun showDialog_cancelClickedMultipleTimes_projectionRequestCancelledIsLoggedOnce() { - dialog.show() + showDialog() clickOnCancel() clickOnCancel() @@ -163,16 +178,22 @@ class ScreenRecordPermissionDialogTest : SysuiTestCase() { @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/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt index 74b2723e870e01b4bdc925624c73d2f4a97c863e..cce038f4ffc187b11cb1511d63f907dbe388dd80 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 @@ class FakeMobileConnectionsRepository( 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 ac75d4f189cc9201537e242d12896f866a083884..03f300542a6f0585da5749827aa5ef5c681e5c3d 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 @@ import org.mockito.MockitoAnnotations // 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 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { 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 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { private val dispatcher = StandardTestDispatcher() private val testScope = TestScope(dispatcher) + private lateinit var underTest: MobileConnectionsRepositoryImpl + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -1178,6 +1180,16 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { assertThat(latest).isFalse() } + @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 { 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 58d93c98f015e028c2932cd1f555417c0830ad29..2f79955c4f5b1bd86b8759bbf5bc3738df4cc67f 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 @@ public class BatteryControllerTest extends SysuiTestCase { 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/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt index 1968d75a7964c72a9c36f8efb00a77d38730c723..017eefe38f2a49a6e1d3b9612634775ef73002da 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.SysuiTestCase 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 junit.framework.Assert.assertNotNull 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { @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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { val refreshUsersCallCount = userRepository.refreshUsersCallCount - telephonyRepository.setCallState(1) + utils.telephonyRepository.setCallState(1) runCurrent() assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1) @@ -801,7 +789,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 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 @@ class UserSwitcherInteractorTest : SysuiTestCase() { resetOrExitSessionReceiver = resetOrExitSessionReceiver, ), uiEventLogger = uiEventLogger, - featureFlags = featureFlags, + featureFlags = utils.featureFlags, userRestrictionChecker = mock(), ) } 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 8353cf78d983d1ee72cff1477d9c6696a7bd08ab..d0fa27e3b79a10d9e8a0cae96565edb70c179b03 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 com.android.systemui.dagger.SysUISingleton 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 @@ class FakeConfigurationRepository @Inject constructor() : ConfigurationRepositor 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 0000000000000000000000000000000000000000..3ab1b6c11e021bd65b3538759e24e2973914a2c2 --- /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 799bb403a8ff251c97b2f61bb185e657c5dc8ae5..2cb17b5badc41e09066f091bf21d4825396cf90f 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 @@ class FakeCommunalRepository( 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/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..0c821eab65e053123e935e1bf61710899372da4c --- /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 0000000000000000000000000000000000000000..e5cadabfc1c722f415d229b922803a9e06944123 --- /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/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt index 957fbbd8680f5bff2b5bed56209728794154a344..ace650020a9d462585d9f2982b3b57fcc75c1ca5 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 @@ class FakePowerRepository @Inject constructor() : PowerRepository { 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 f97d6b3d262f28b7c000d0031323c759508a0104..36ec18fc4de4049ec8fa11103fc8a6b5d88087a3 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,10 +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 @@ -27,8 +32,11 @@ import com.android.systemui.authentication.data.repository.FakeAuthenticationRep 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 @@ -36,11 +44,12 @@ import com.android.systemui.classifier.domain.interactor.FalsingInteractor 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 @@ -62,10 +71,13 @@ import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags 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 @@ -102,15 +114,23 @@ class SceneTestUtils( 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) @@ -186,7 +206,7 @@ class SceneTestUtils( featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, bouncerRepository = FakeKeyguardBouncerRepository(), - configurationRepository = FakeConfigurationRepository(), + configurationRepository = configurationRepository, shadeRepository = FakeShadeRepository(), sceneInteractorProvider = { sceneInteractor() }, powerInteractor = PowerInteractorFactory.create().powerInteractor, @@ -194,10 +214,10 @@ class SceneTestUtils( } fun communalInteractor(): CommunalInteractor { - return CommunalInteractor( - communalRepository = communalRepository, - widgetRepository = communalWidgetRepository, - ) + return CommunalInteractorFactory.create( + communalRepository = communalRepository, + ) + .communalInteractor } fun bouncerInteractor( @@ -220,6 +240,7 @@ class SceneTestUtils( fun bouncerViewModel( bouncerInteractor: BouncerInteractor, authenticationInteractor: AuthenticationInteractor, + actionButtonInteractor: BouncerActionButtonInteractor, users: List<UserViewModel> = createUsers(), ): BouncerViewModel { return BouncerViewModel( @@ -232,13 +253,16 @@ class SceneTestUtils( 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 } } @@ -288,6 +312,40 @@ class SceneTestUtils( } } + 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 0000000000000000000000000000000000000000..c8013ef96fa7081caed0695f75be7bbe70267f53 --- /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 992ac62fe3c448e9fda8271db5a7f1033f9fa7ea..5cde3868199b47f7fce635b31bbe6eca13756ce3 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 dagger.Module 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 @@ class FakeTelephonyRepository @Inject constructor() : TelephonyRepository { 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 @@ class FakeTelephonyRepository @Inject constructor() : TelephonyRepository { _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 0000000000000000000000000000000000000000..f0a8fd0abf3d85cbe84fa573133433d4e142972f --- /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/ravenwood/Android.bp b/ravenwood/Android.bp index d0e442e7058d78c96a6051e43ea1e9ef93f57324..5c9bf182834784d3399f58f48d08940364c65331 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -32,3 +32,14 @@ java_library { 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 0000000000000000000000000000000000000000..0aac084dd4ced9c8a5d2b250afa1a6b3dcf38e8e --- /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 0000000000000000000000000000000000000000..a6b3f668efa6b073a398ca029520e75d4a777ef7 --- /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/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index b37bbd6ea27f5df37f800458be17dd981453d32e..ab678d93c665c201a044b480f71304e2ff94cc3b 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -20,3 +20,10 @@ flag { 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/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 4f322203192facf10a4c0dbce90ecc98f69ca848..065a447a7cd1848162cbe6bb98b3095936902b25 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1421,7 +1421,7 @@ public abstract class PackageManagerInternal { @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 class PackageManagerInternal { */ 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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 02235096ac15a724907d8d1878c423cf9ec62bad..15fc2dc15d029e67103aa45549125dbd9acf915b 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.KeyguardManager; 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 @@ class StorageManagerService extends IStorageManager.Stub | 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 @@ class StorageManagerService extends IStorageManager.Stub // 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 @@ class StorageManagerService extends IStorageManager.Stub @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 afc0dd188c3cde176cd432f735763b09e649026e..cd9c53bb92b9e51ea91c4652ad40d2274bf49fac 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 4bdb4da971441a9276405df4699492bd8cc642dd..5f1a7e7e81230ce550ce06f19a1445bc60261eba 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.ChangeId; 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 514be1586e424d0785e702a52f48a278e3f8f732..b99a98fe6e8b64e522d9250a9c6c7eb991b306a9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -429,11 +429,10 @@ import com.android.internal.util.FastPrintWriter; 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; @@ -3152,11 +3151,8 @@ public class ActivityManagerService extends IActivityManager.Stub } 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; @@ -3634,30 +3630,10 @@ public class ActivityManagerService extends IActivityManager.Stub } 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) { @@ -4178,29 +4154,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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( @@ -5938,18 +5892,9 @@ public class ActivityManagerService extends IActivityManager.Stub @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 @@ -20163,26 +20108,20 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ -20202,30 +20141,23 @@ public class ActivityManagerService extends IActivityManager.Stub } @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); } @@ -20256,37 +20188,28 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 947fcd38f5a0ac4fe4b9629bc85338d68d8313ab..18a91535a34cf9149a6d020ebb0fa4c9ead341a6 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 static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION; 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 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy 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 class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy 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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index d6997daaa12bf0d7ee2a75c04329ab2655bba007..052b0c2078d1c8d8050de6f4553f2410c3fc7f9d 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 @@ public class AppOpsService extends IAppOpsService.Stub { } }, 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 @@ public class AppOpsService extends IAppOpsService.Stub { } } - /** @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*/); - } - - @Override - public int checkOperationWithStateRaw(int code, AttributionSourceState attributionSourceState) { - final AttributionSource attributionSource = new AttributionSource(attributionSourceState); - return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, true /*raw*/); + @Nullable String attributionTag) { + return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag, + 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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); - } - - @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); + return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName, + attributionTag, 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 @@ public class AppOpsService extends IAppOpsService.Stub { } } - /** @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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } // 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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); - } - - @Override - public void finishOperationWithState(IBinder clientId, int code, - AttributionSourceState attributionSourceState) { - final AttributionSource attributionSource = new AttributionSource(attributionSourceState); - mCheckOpsDelegateDispatcher.finishOperation(clientId, code, attributionSource); + mCheckOpsDelegateDispatcher.finishOperation(clientId, code, uid, packageName, + attributionTag); } - 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 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 @@ public class AppOpsService extends IAppOpsService.Stub { } 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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/media/BluetoothProfileMonitor.java b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java index b129dd0f74c651bcf06886655d080b0b17ea1ef1..d61e7fb6591ed61f1f08f42255d7eb84f46d48f8 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 @@ import java.util.Objects; @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 @@ import java.util.Objects; } } - /* 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 @@ import java.util.Objects; 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 @@ import java.util.Objects; 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/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java index b006ac8625339718ea7553bc07d92985f07b2094..a8cba532435d841fade7a74ac42c5f6a9cca59ce 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 android.util.Slog; 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.PrintWriter; 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 @@ public class PersistentDataBlockService extends SystemService { 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 @@ public class PersistentDataBlockService extends SystemService { 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 class PersistentDataBlockService extends SystemService { 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 @@ public class PersistentDataBlockService extends SystemService { super.onBootPhase(phase); } + @VisibleForTesting + void setAllowedUid(int uid) { + mAllowedUid = uid; + } + private void formatIfOemUnlockEnabled() { boolean enabled = doGetOemUnlockEnabled(); if (enabled) { @@ -220,7 +244,7 @@ public class PersistentDataBlockService extends SystemService { } } - SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0"); + setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0"); } private void enforceOemUnlockReadPermission() { @@ -278,7 +302,7 @@ public class PersistentDataBlockService extends SystemService { 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 @@ public class PersistentDataBlockService extends SystemService { return mBlockDeviceSize; } - private long getFrpCredentialDataOffset() { - return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE; + @VisibleForTesting + int getMaximumFrpDataSize() { + return (int) (getTestHarnessModeDataOffset() - DIGEST_SIZE_BYTES - HEADER_SIZE); + } + + @VisibleForTesting + long getFrpCredentialDataOffset() { + return getOemUnlockDataOffset() - FRP_CREDENTIAL_RESERVED_SIZE; } - private long getTestHarnessModeDataOffset() { + @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 @@ public class PersistentDataBlockService extends SystemService { return md.digest(); } - private void formatPartitionLocked(boolean setOemUnlockEnabled) { + @VisibleForTesting + void formatPartitionLocked(boolean setOemUnlockEnabled) { try { FileChannel channel = getBlockOutputChannel(); @@ -448,10 +485,15 @@ public class PersistentDataBlockService extends SystemService { 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 @@ public class PersistentDataBlockService extends SystemService { 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 @@ public class PersistentDataBlockService extends SystemService { 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 @@ public class PersistentDataBlockService extends SystemService { 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 26e70c0279e3fe42cdcc53b3960d26bc28a583dc..21284a05ddadec174c18e64d0e4d791645a6b08e 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 @@ public abstract class ApexManager { * 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 @@ public abstract class ApexManager { * 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 5b32a94c4eac4a6ea8c7005c26a8f4d7e0c5eed1..a5bc2c36a5a8f9ca07ef8ebf4f944d3a4910e320 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 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot { 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 9bca9f0192b2a572ab8fc24cd6f0e317045849cb..36677df07ca3e6d2142fc5ec337e0252548b58bb 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 @@ public final class BackgroundDexOptService { 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 @@ public final class BackgroundDexOptService { 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 e367609e89b6c3c1e40fc568f506e6bc913da206..7b35589ae6824f5fd56405597376a16eae2506e0 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 @@ public final class BroadcastHelper { 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 @@ public final class BroadcastHelper { 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 @@ public final class BroadcastHelper { } 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 486282aa2c04bdd1e2c32bf7c2ec40b23c4d0581..0bd165ec91fa76f32de84460cc781b878ccc3d5b 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 android.content.pm.CrossProfileAppsInternal; 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 5f28e56e996c95049e5db45a6995cb20fc1411f5..f1dc2849a3912065720ade5c5782abbdfd32c766 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } 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 72f3afc35e99562f1771e6d91f2c1153e4f209e1..3313e7234a19a272f91b93e847123fae7f84c1e3 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 @@ import android.os.UserHandle; 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 @@ public final class CrossProfileDomainInfo { 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 51214faca730de24de4dd70c5101b8d686c11f33..56e9c0a3544b2ef4d490aaad43cb6f7d6b7d4866 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 @@ import android.util.ArraySet; * 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 a8da818932fa0473fc95f9d615f5f5ad3aa0a11b..1d38bbb2a1bdbb47fa3e848f765b9ae45d8820f2 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 @@ import java.util.function.Function; */ 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 29322e2553e93974d51e31d1ee965bfbc2d1fe28..888e1c26206cc5bef58b79793aa8e9f38ade0f58 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 @@ public class DataLoaderManagerService extends SystemService { 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 dc5915de5551aa1e2aa6f3196e4c05fc7d541cc6..db05dd3949157d3ba0f625485438ab106aee0838 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 @@ public final class DefaultCrossProfileIntentFilter { } 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 dd96a2b84a9783dfc5ca34fdccd78747d34a26f7..cc6bb007411e379207aa6b0a954b1a72ee6acb43 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 @@ final class InstallArgs { // 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/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index be4fb5c539a9644ea52e8d3c3354068e5bb0a3c7..fc831205f60e7169ef0ea9cd4d1a83894d73f5a3 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 @@ final class InstallRequest { 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 ca8dc2973404e3fd99272ae1fc4f4378e48f6a94..187cadaf4d46927e8a1596e7bb3f689a883897ba 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 @@ class InstallingSession { 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 8b82a1c6e7c954ae193641a9bebd1ef4f735535a..3f4cc4a84ffd7de5018c59257c20bf082cd5ad3b 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 a4d8632ac0775db82010881ec435b1ee77f65831..98eee4dc3b1dc836374253b9ce5fff1c7879dff5 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } 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 1be28ca9e915ddce492005f4bd5267e5fa2f4cbf..4b466be8476b317564af2f57d8a97dd4165736b7 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @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 e5f7962e254c6e28cf6e1aaba2dc71c48adaca09..434c00afa8b22198d030bc2e58a9f6f8c23ccdd2 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 @@ public class PackageManagerService implements PackageSender, TestUtilityService 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 9428ef6c8ba9b03bd8c25726b3be4abebd7443ec..655b9c93d9dd817e8426dd70aad27aee35dd35db 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 final class PackageManagerServiceTestParams { 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 final class PackageManagerServiceTestParams { 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 d4abad8499f26827a3a733b71b57017829220361..6f45d2befc6b6047dd9ce63bb0e804f0fb4e23ed 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 a09e71311125e6bb3f2927c7a9df54a66708c624..c829e1c79ba8688ece7c88ea69a14edd63b54a9b 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 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { 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 1f12c88ede366456066b747c43f09575a7d1bc98..fa9409f88a13ba2825b856ce3ab9bf83bf1a8489 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 @@ class PackageMonitorCallbackHelper { } 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 @@ class PackageMonitorCallbackHelper { || 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,27 +220,31 @@ class PackageMonitorCallbackHelper { intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); final int[] allowUids = - broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{}; + broadcastAllowList != null ? broadcastAllowList.get(userId) : null; + doNotifyCallbacks(callbacks, intent, userId, allowUids, 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 (broadcastAllowList != null && registerUid != Process.SYSTEM_UID - && !ArrayUtils.contains(allowUids, registerUid)) { - if (DEBUG) { - Slog.w("PackageMonitorCallbackHelper", - "Skip invoke PackageMonitorCallback for " + action + ", uid " - + registerUid); - } - return; + 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); } - invokeCallback(callback, intent); - })); - } + return; + } + invokeCallback(callback, intent); + })); } private void invokeCallback(IRemoteCallback callback, Intent intent) { diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 5507e7c7754a61fd171f98f4270abc2949e84c31..18caafdaa56ac728210bc9afd1d651187f884933 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 class PreferredComponent { 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 cf5aa7b1eb02aeb3df280d22ed9f7640a0acbe6c..e667bfe36d186877d1bcef28a9f9900a19b5e0bc 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 @@ public final class SELinuxMMAC { // 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 440823c436073e9e6bf4d22d4288a617dad9f08b..6338965e4a8001404742762a376763b864004ec4 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 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile 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 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile @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 8667888fbb51b3786531f601148ec5c09372da83..12115afcbeec78d3e088b49279c57d062a910d76 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 @@ abstract class ShortcutPackageItem { 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 c6aba2ab9cbec429f16c6c45b809aa9637d3fa18..3adeb4b5925fad8823ca940f7dcd9fdeed6e6503 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 @@ public class ShortcutService extends IShortcutService.Stub { 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 @@ public class ShortcutService extends IShortcutService.Stub { 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 e04a1e5b3569afbbac18a7d55204d5041c308300..31541d083fbc628c8c286aa0b769796a2cb13549 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 @@ public class SnapshotStatistics { 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 @@ public class SnapshotStatistics { * 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 @@ public class SnapshotStatistics { /** * 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/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java index 895edce093b5e9f80f80070585ff58f134d58a80..651578dc5debbbc8f386a70e0eaef7654d07b679 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 class UserJourneyLogger { 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 6e738daf93158b99e6f8f92038d2a1caee74c6a0..78c13f854fe41ff2a53232c3cb7a799ab31f754a 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 @@ public class DexManager { 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 @@ public class DexManager { * 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 d3a64bb0b9acc6bf17c7a4f84d186bd6165c6795..2ab7db47f8e5708c9fdb916e7057149c19c3977b 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 class PackageCacher { 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 9e20805c287400d914297ab83092fb096ec941a1..c26cf1c9a113830b3e74d7a60c8e55bc615f9e19 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 class PackageInfoUtils { */ 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 d82a5000844fc126a031082fd0474abc0481aa2a..3b10c7f2c2a6f143efef4f6fec37c38376a7991f 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 @@ public class PackageParser2 implements AutoCloseable { 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 056aae49ae82a25a7fbd21cbfd5060161e04e40b..75dd67d97620680bab2911932e70c7e3d98e82ff 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 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal, 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 d6a7dc6681b924ae1494c52af8403b1e38a7cc08..6f6bb45bad507908c285451a6387510961e7c8ff 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 @@ final class DefaultPermissionGrantPolicy { */ 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 70986c3cfbac374bd19d483dc088bda116a51dc6..4c831d36c9e324a631e0e35b9fcd03bef0ded28f 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 @@ import java.util.Set; 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 8240c47a607b6cfa0f05b75e90437be3872af8ce..061698a929d6d707d80a4c823d90abef083ba9d1 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 @@ public class ParsingPackageUtils { 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 fac681aaf1c479d285697a2c8ddd557f108d90f7..0ceda421913d825d168bdeead9d2decb5a9d817a 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 @@ public class ComponentResolver extends ComponentResolverLocked implements 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 9115775acd05bb81118a778d6e41a330816a3c86..80cde73ecf1f86b7defeab3147a1a6288d487e3f 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 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com 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 11f62e953525b2760242f8de2a3a321dcbb20b96..3d4d4eca7f48776575001ad391feb0db9bccb0be 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 @@ public class DomainVerificationService extends SystemService 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 6c56360177d84416670552526929d3ccffa1f7ba..d71dbbb5d53b80122bac9c9073edf2db06be98a2 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 @@ public class DomainVerificationPkgState { 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 c2821aef21421c40ac99655ce0c71fb429862746..b83421fe78d795f17d59d380d10b5b46eb4d6ed1 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.Log; 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 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } @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 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } @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 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } @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 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } @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 45ca690ba20fcd77c6cba194ce0d813acd8b4be2..cf1036c03c83d537ebb277b4c576c3485f8d3d0a 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLidNavigationAccessibility; int mShortPressOnPowerBehavior; private boolean mShouldEarlyShortPressOnPower; + private boolean mShouldEarlyShortPressOnStemPrimary; int mLongPressOnPowerBehavior; long mLongPressOnPowerAssistantTimeoutMs; int mVeryLongPressOnPowerBehavior; @@ -2748,6 +2749,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override void onPress(long downTime) { + if (mShouldEarlyShortPressOnStemPrimary) { + return; + } stemPrimaryPress(1 /*count*/); } @@ -2760,6 +2764,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 3dbd2f88385eadee3630e6abf79cbf1847460cab..6a0fe90a1e650a464fc4946c2f6bf872484b8145 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 @@ public class PowerGroup { @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 @@ public class PowerGroup { 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 @@ public class PowerGroup { 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 8ce0c72101db95eaf3b252253c430cc8ffae4e0a..ec5172fca5fa3276f1f7604b215f2d6641d24054 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 @@ public final class PowerManagerService extends SystemService // 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 @@ public final class PowerManagerService extends SystemService 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 @@ public final class PowerManagerService extends SystemService .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 @@ public final class PowerManagerService extends SystemService int getDesiredScreenPolicyLocked(int groupId) { return mPowerGroups.get(groupId).getDesiredScreenPolicyLocked(sQuiescent, mDozeAfterScreenOff, mBootCompleted, - mScreenBrightnessBoostInProgress); + mScreenBrightnessBoostInProgress, mBrightWhenDozingConfig); } @VisibleForTesting @@ -4655,6 +4662,7 @@ public final class PowerManagerService extends SystemService 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 a388932cb708173ea622a3c208ac4b1638722563..b8e581f32a5832b4af5f1af2e5c2032aa64b5570 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 class CpuPowerStatsCollector extends PowerStatsCollector { 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/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index d88f4268434b1ee24ecd7b8d8b8dfe9d21e0da5d..83270f6b7b56af9af6748b5411af1b12bcba20f1 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 @@ package com.android.server.timedetector; 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.ITimeDetectorService; 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub */ 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 final class TimeDetectorService extends ITimeDetectorService.Stub 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub // 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub 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 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub 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 1500cfaeb3a24df4b046418375ac1efb77375e38..1f1d83fefc593d5085efa0d49ac7b59860f9f7b1 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 @@ public interface CallerIdentityInjector { /** 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(); @@ -44,6 +51,12 @@ public interface CallerIdentityInjector { protected Real() { } + @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/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index dac4bf8cb6e6d6b6636a312c6a17c68ebaf0a89a..d914544566ff7b47efd973c9ab807290516ca34e 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 @@ package com.android.server.timezonedetector; 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.ITimeZoneDetectorService; 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 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub 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 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub 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 52ab9b855c7c134456745857f7c5d7f52fe63635..86be6ba5e6ad8568a0d4f55f5a1d11ef6041584f 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 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord } 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 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord 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 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord 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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a76fe287f31333ea5b521d383154faf21cc6caa9..3c56a4e5eb0488127db7235be5473f49958804f9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3859,10 +3859,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index d69c5ef5ee9191f8cea5441d0a52a70d61470672..8a7cc67c3660e1471d0ec0d306e869b6790a6907 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 @@ public class DisplayPolicy { 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 @@ public class DisplayPolicy { } } - - 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 cd114fcf9e217a4c52e83f3e36f7ab12c10885fd..9d5ddf3bf264e2d3ab0b8b78d7e66b18742eebab 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.PENDING_CONTROL_TA 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 @@ class InsetsSourceProvider { 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 @@ class InsetsSourceProvider { 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 e82f3221f0a45c4fb685f372295b6abe037a9048..5227a52545f4f49de2e11d39fcb84aeb87fb5aa1 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } final TaskFragment organizedTf = r.getOrganizedTaskFragment(); + final TaskFragment taskFragment = r.getTaskFragment(); final boolean singleActivity = task.getNonFinishingActivityCount() == 1; if (singleActivity) { rootTask = task; @@ -2137,7 +2138,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> .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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // 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 f4f641f35395eb2c88f6defa9f33c5683890cdfb..bffdf54e17ce0982f0318952c5b94771e8a9550d 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_WITH_CLASS_NAME 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.util.Slog; 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 @@ class SnapshotPersistQueue { } } - @TestApi + @VisibleForTesting void waitForQueueEmpty() { while (true) { synchronized (mLock) { @@ -112,9 +112,20 @@ class SnapshotPersistQueue { } } - @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) { @@ -122,6 +133,16 @@ class SnapshotPersistQueue { } } + @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) { @@ -235,6 +256,8 @@ class SnapshotPersistQueue { @GuardedBy("mLock") @Override void onQueuedLocked() { + // Remove duplicate request. + mStoreQueueItems.remove(this); mStoreQueueItems.offer(this); } @@ -358,6 +381,14 @@ class SnapshotPersistQueue { 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 2f52de4cb765ee9bc29621c53037573cde3dd2fb..197edc30aa8e7ef96256e29877bd2f3d7b61a055 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1535,10 +1535,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { 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); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b23ffa8b203e6ff00d604729f05ab76212458e96..c0bf2ce414358ee77bd04569c3ac724ece894c96 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_TRANSLUCENT; 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } // 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { 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 6e3d24b9f7113d5dd5c6c1b728d234e99f964429..ab3ddbd9422459b7e9307cfd117928b982ba377b 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; 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 @@ public class WindowManagerService extends IWindowManager.Stub @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 @@ public class WindowManagerService extends IWindowManager.Stub 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 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** 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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ae2df00007f456294cafd89d6e090fd577d925aa..4e17011c71414a393afc9f9f77359188b47990ba 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // 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 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } 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 dc05462c1a1c9408982412b3fd97db39130dd13e..80427b346f1a4e26ae865d1276cbfa8063683b0a 100644 --- a/services/core/jni/tvinput/JTvInputHal.cpp +++ b/services/core/jni/tvinput/JTvInputHal.cpp @@ -25,7 +25,7 @@ JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, std::shared_ptr<ITvInputWrap 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 @@ void JTvInputHal::NotifyTvMessageHandler::handleMessage(const Message& message) } } -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 @@ JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aid : 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 6026a107c67fd4d5915f47c87d5dde9e4968b0f1..2ef94ac4a3b0954083253b1a4316862c96c55c63 100644 --- a/services/core/jni/tvinput/JTvInputHal.h +++ b/services/core/jni/tvinput/JTvInputHal.h @@ -168,23 +168,39 @@ private: 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 @@ private: ::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 @@ private: 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 37cf8445920f88b38840cc36ff9aeb902681b86d..cdd926622dccf17b2918f2956091c989de40c658 100644 --- a/services/core/jni/tvinput/TvInputHal_hidl.cpp +++ b/services/core/jni/tvinput/TvInputHal_hidl.cpp @@ -59,7 +59,11 @@ JTvInputHal::TvInputEventWrapper JTvInputHal::TvInputEventWrapper::createEventWr 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 @@ JTvInputHal::ITvInputWrapper::ITvInputWrapper(sp<HidlITvInput>& hidlTvInput) : 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/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index 7ceccc57c0f1c27cac54ca08ec7c367892ff4656..47ae97fc5d27a4853a4c2f6b23f5126168a101f2 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.AppOpsManager.PackageOps; 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 @@ public class AppStateTrackerTest { 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 032d026648dff82be79786a1de8bd6679aafb2e8..2f909f818bfe31e9cd57047ed9e65a4ca1c2eb3b 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.BroadcastOptions; 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 @@ public class ActivityManagerServiceTest { 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 @@ public class ActivityManagerServiceTest { */ @Test public void testDispatchUids_dispatchNeededChanges() throws RemoteException { - mockNoteOp(); + mockNoteOperation(); final int[] changesToObserve = { ActivityManager.UID_OBSERVER_PROCSTATE, @@ -905,7 +900,7 @@ public class ActivityManagerServiceTest { */ @Test public void testDispatchUidChanges_procStateCutpoint() throws RemoteException { - mockNoteOp(); + mockNoteOperation(); final IUidObserver observer = mock(IUidObserver.Stub.class); @@ -975,7 +970,7 @@ public class ActivityManagerServiceTest { */ @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 dcbee83b839b0257e518de98a7ccb582323c4193..bb91939c430e05fdf0c5722a4927007042088cbe 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.Notification; 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 @@ public final class BackgroundRestrictionTest { 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 daed5df4edd77338b145d98ddfce247114e26027..646f4862d75d5d5dd698866bb71bee9fb0123860 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 static org.mockito.ArgumentMatchers.nullable; 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 @@ public class AppOpsServiceTest { } @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 @@ public class AppOpsServiceTest { * 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 class AppOpsServiceTest { 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 @@ public class AppOpsServiceTest { @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 @@ public class AppOpsServiceTest { @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 @@ public class AppOpsServiceTest { @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 @@ public class AppOpsServiceTest { @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 @@ public class AppOpsServiceTest { @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 @@ public class AppOpsServiceTest { @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 fe31b9cbe558554892eaaa6e8fbcfc89037a6318..a776eec4546fb8cba5c1b11c1a48f8f769fd4121 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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 @@ public class PowerGroupTest { /* 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/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f537efd9736da427595343da416848d3d06629aa --- /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 01a91c1db1e6ae4d31b7f43ca29880cb5ce40bd4..398148ff4d3b0d26bc8f1279c11dabe9a89662b1 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.assertTrue; 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 @@ public class SuspendPackagesTest { }; 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/timetests/AndroidManifest.xml b/services/tests/timetests/AndroidManifest.xml index 62fbb05ee1e1546af439234783505f0272a378cb..a21d383c3233e476a640e1facc91c842f2798465 100644 --- a/services/tests/timetests/AndroidManifest.xml +++ b/services/tests/timetests/AndroidManifest.xml @@ -17,9 +17,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.framework.services.tests.time"> - <!-- Required for user checks --> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> - <application> <uses-library android:name="android.test.runner" /> </application> diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java index 67b8cc22fac0aaa6f466b3ca6841e7ca095ed24c..56db9ccd68e70d1ca7da9fc737c61bff17fca7a5 100644 --- a/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java +++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java @@ -34,6 +34,11 @@ public class TestCallerIdentityInjector implements CallerIdentityInjector { mCurrentCallingUserId = userId; } + @Override + public int resolveUserId(int userId, String debugInfo) { + return userId; + } + @Override public int getCallingUserId() { assertNotNull("callingUserId has been cleared", mCurrentCallingUserId); 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 bf86563e3d86f4c3ed684ebfb66f43f63eeb6a95..c782d3e90e9640688fb63de7bbb964b385714913 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 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 @@ public class DisplayPolicyTests extends WindowTestsBase { 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 c4d03bebc751623989944bf8fc981803bd201125..49a888689e60074f4785c636fed549a242fa2a55 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.SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE; 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 @@ public class RefreshRatePolicyTest extends WindowTestsBase { 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 @@ public class RefreshRatePolicyTest extends WindowTestsBase { 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 @@ public class RefreshRatePolicyTest extends WindowTestsBase { 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 03188f813555fbb2973f1253a57f5d03292f2024..9af5ba57d6c95ff8d869ff008600ecf308576de4 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 @@ public class SystemServicesTestRule implements TestRule { 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 @@ public class SystemServicesTestRule implements TestRule { } } - 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/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index 8fecbb9d4decd7c8403042abdf2fdfebd5ba0355..4b54e4464ca7815941a55226c6a63194aff80c20 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 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa 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 2689e9d14c00d6941e5342e04cb03e19a3eede22..a83caa4a4e959504bb2eac29c53d449fd9cdbdc3 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 @@ public class TransitionTests extends WindowTestsBase { 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/test-mock/api/current.txt b/test-mock/api/current.txt index f61cce666cca33b8155347c453cfd3b19dce4b7c..daaab3314679e7ce6fb5be46b761299afca3efce 100644 --- a/test-mock/api/current.txt +++ b/test-mock/api/current.txt @@ -156,6 +156,7 @@ package android.test.mock { 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 @@ package android.test.mock { 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/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index 790da34b1b0847bb5e46073b9dfe877b1ec4c903..12a57d52491a15319d1be3d53d7adccdf1032f72 100644 --- a/tests/FlickerTests/ActivityEmbedding/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 @@ package com.android.server.wm.flicker.activityembedding.pip 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 @@ -149,8 +150,12 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : 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) } } @@ -161,6 +166,61 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : } } + /** 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/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING index 970362611f53ac2efb3b1e7264740d2e6c39c68b..e02492d68bb4e182519080ca4bbd05890eac16cb 100644 --- a/tools/hoststubgen/TEST_MAPPING +++ b/tools/hoststubgen/TEST_MAPPING @@ -1,6 +1,8 @@ { // TODO: Change to presubmit. "postsubmit": [ - { "name": "tiny-framework-dump-test" } + { "name": "tiny-framework-dump-test" }, + { "name": "hoststubgentest" }, + { "name": "hoststubgen-invoke-test" } ] } diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp index 182940e6f7c5112ab09bfa693238cb1999ee0932..fd4ec8bd5931e1837e14045152a3918eca8d0cde 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -99,6 +99,18 @@ java_binary_host { visibility: ["//visibility:public"], } +java_test_host { + name: "hoststubgentest", + // main_class: "com.android.hoststubgen.Main", + srcs: ["test/**/*.kt"], + static_libs: [ + "hoststubgen", + "truth", + ], + test_suites: ["general-tests"], + visibility: ["//visibility:private"], +} + // File that contains the standard command line argumetns to hoststubgen. // This is only for the prototype. The productionized version is "ravenwood-standard-options". filegroup { 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 d749f076f3b52d129c021820aa1a43c164cc489f..12c7841556fc2246cfdada52f6b3a6d05524914e 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 android.os.IBinder; 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 @@ public class Parcel_host { 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 @@ public class Parcel_host { + data.length + " given=" + destLen); return false; } + System.arraycopy(data, 0, dest, 0, data.length); return true; } @@ -289,7 +291,12 @@ public class Parcel_host { 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 class Parcel_host { 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 class Parcel_host { } 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/invoketest/Android.bp b/tools/hoststubgen/hoststubgen/invoketest/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..7e90e421a1f9d073cf4637e1d23ed291094ee8a4 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/invoketest/Android.bp @@ -0,0 +1,20 @@ +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"], +} + +sh_test_host { + name: "hoststubgen-invoke-test", + src: "hoststubgen-invoke-test.sh", + test_suites: ["general-tests"], + + // Note: java_data: ["hoststubgen"] will only install the jar file, but not the command wrapper. + java_data: [ + "hoststubgen", + "hoststubgen-test-tiny-framework", + ], +} diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh new file mode 100755 index 0000000000000000000000000000000000000000..34b2145b8d2264dbb46ee5bb363ffe5bf10649e3 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh @@ -0,0 +1,163 @@ +#!/bin/bash +# 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. + +set -e # Exit when any command files + +# This script runs HostStubGen directly with various arguments and make sure +# the tool behaves in the expected way. + + +echo "# Listing files in the test environment" +ls -lR + +echo "# Dumping the environment variables" +env + +# Set up the constants and variables + +export TEMP=$TEST_TMPDIR + +JAR=hoststubgen-test-tiny-framework.jar +STUB=$TEMP/stub.jar +IMPL=$TEMP/impl.jar + +ANNOTATION_FILTER=$TEMP/annotation-filter.txt + +HOSTSTUBGEN_OUT=$TEMP/output.txt + +# Because of `set -e`, we can't return non-zero from functions, so we store +# HostStubGen result in it. +HOSTSTUBGEN_RC=0 + +# Define the functions to + + +# Note, because the build rule will only install hoststubgen.jar, but not the wrapper script, +# we need to execute it manually with the java command. +hoststubgen() { + java -jar ./hoststubgen.jar "$@" +} + +run_hoststubgen() { + local test_name="$1" + local annotation_filter="$2" + + echo "# Test: $test_name" + + rm -f $HOSTSTUBGEN_OUT + + local filter_arg="" + + if [[ "$annotation_filter" != "" ]] ; then + echo "$annotation_filter" > $ANNOTATION_FILTER + filter_arg="--annotation-allowed-classes-file $ANNOTATION_FILTER" + echo "=== filter ===" + cat $ANNOTATION_FILTER + fi + + hoststubgen \ + --debug \ + --in-jar $JAR \ + --out-stub-jar $STUB \ + --out-impl-jar $IMPL \ + $filter_arg \ + |& tee $HOSTSTUBGEN_OUT + HOSTSTUBGEN_RC=${PIPESTATUS[0]} + echo "HostStubGen exited with $HOSTSTUBGEN_RC" + return 0 +} + +run_hoststubgen_for_success() { + run_hoststubgen "$@" + + if (( $HOSTSTUBGEN_RC != 0 )) ; then + echo "HostStubGen expected to finish successfully, but failed with $rc" + return 1 + fi +} + +run_hoststubgen_for_failure() { + local test_name="$1" + local expected_error_message="$2" + shift 2 + + run_hoststubgen "$test_name" "$@" + + if (( $HOSTSTUBGEN_RC == 0 )) ; then + echo "HostStubGen expected to fail, but it didn't fail" + return 1 + fi + + # The output should contain the expected message. (note we se fgrep here.) + grep -Fq "$expected_error_message" $HOSTSTUBGEN_OUT +} + +# Start the tests... + +# Pass "" as a filter to _not_ add `--annotation-allowed-classes-file`. +run_hoststubgen_for_success "No annotation filter" "" + +# Now, we use " ", so we do add `--annotation-allowed-classes-file`. +run_hoststubgen_for_failure "No classes are allowed to have annotations" \ + "not allowed to have Ravenwood annotations" \ + " " + +run_hoststubgen_for_success "All classes allowed (wildcard)" \ + " +* # Allow all classes +" + +run_hoststubgen_for_failure "All classes disallowed (wildcard)" \ + "not allowed to have Ravenwood annotations" \ + " +!* # Disallow all classes +" + +run_hoststubgen_for_failure "Some classes not allowed (1)" \ + "not allowed to have Ravenwood annotations" \ + " +android.hosttest.* +com.android.hoststubgen.* +com.supported.* +" + +run_hoststubgen_for_failure "Some classes not allowed (2)" \ + "not allowed to have Ravenwood annotations" \ + " +android.hosttest.* +com.android.hoststubgen.* +com.unsupported.* +" + +run_hoststubgen_for_success "All classes allowed (package wildcard)" \ + " +android.hosttest.* +com.android.hoststubgen.* +com.supported.* +com.unsupported.* +" + + +run_hoststubgen_for_failure "One specific class disallowed" \ + "TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \ + " +!com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +* # All other classes allowed +" + + + +echo "All tests passed" +exit 0 \ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 872d56856c3378593ef88a61bb8171032a72999e..f32dc721873e63e374ced5d5786f3ffde3464e61 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -27,6 +27,7 @@ import com.android.hoststubgen.filters.OutputFilter import com.android.hoststubgen.filters.StubIntersectingFilter import com.android.hoststubgen.filters.createFilterFromTextPolicyFile import com.android.hoststubgen.filters.printAsTextPolicy +import com.android.hoststubgen.utils.ClassFilter import com.android.hoststubgen.visitors.BaseAdapter import com.android.hoststubgen.visitors.PackageRedirectRemapper import org.objectweb.asm.ClassReader @@ -167,6 +168,14 @@ class HostStubGen(val options: HostStubGenOptions) { filter ) + val annotationAllowedClassesFilter = options.annotationAllowedClassesFile.let { filename -> + if (filename == null) { + ClassFilter.newNullFilter(true) // Allow all classes + } else { + ClassFilter.loadFromFile(filename, false) + } + } + // Next, Java annotation based filter. filter = AnnotationBasedFilter( errors, @@ -181,7 +190,8 @@ class HostStubGen(val options: HostStubGenOptions) { options.nativeSubstituteAnnotations, options.classLoadHookAnnotations, options.stubStaticInitializerAnnotations, - filter + annotationAllowedClassesFilter, + filter, ) // Next, "text based" filter, which allows to override polices without touching diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt index d74612d48de29dd54aa33d878abddc538ac3b09e..aab02b8de2548440a9907dfab6e5425c0990bbb7 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt @@ -51,6 +51,8 @@ class HostStubGenOptions( var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(), + var annotationAllowedClassesFile: String? = null, + var defaultClassLoadHook: String? = null, var defaultMethodCallHook: String? = null, @@ -171,6 +173,9 @@ class HostStubGenOptions( "--package-redirect" -> ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg)) + "--annotation-allowed-classes-file" -> + ret.annotationAllowedClassesFile = ai.nextArgRequired(arg) + "--default-class-load-hook" -> ret.defaultClassLoadHook = ai.nextArgRequired(arg) @@ -314,6 +319,7 @@ class HostStubGenOptions( nativeSubstituteAnnotations=$nativeSubstituteAnnotations, classLoadHookAnnotations=$classLoadHookAnnotations, packageRedirects=$packageRedirects, + $annotationAllowedClassesFile=$annotationAllowedClassesFile, defaultClassLoadHook=$defaultClassLoadHook, defaultMethodCallHook=$defaultMethodCallHook, intersectStubJars=$intersectStubJars, diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt index f75062b3a878d82d14b1ca827fb416b6da5bc5d4..937e56c2cbb566225b733e64984b5a183b0e8f29 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt @@ -58,4 +58,29 @@ fun <T> addNonNullElement(a: List<T>, b: T?): List<T> { return listOf(b) } return a + b -} \ No newline at end of file +} + + +/** + * Exception for a parse error in a file + */ +class ParseException : Exception, UserErrorException { + val hasSourceInfo: Boolean + + constructor(message: String) : super(message) { + hasSourceInfo = false + } + + constructor(message: String, file: String, line: Int) : + super("$message in file $file line $line") { + hasSourceInfo = true + } + + fun withSourceInfo(filename: String, lineNo: Int): ParseException { + if (hasSourceInfo) { + return this // Already has source information. + } else { + return ParseException(this.message ?: "", filename, lineNo) + } + } +} 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 a51bdcf0c7931a1185b735cf839a54c5329f38fc..1bcf3642082fe7bab2edf65329c3aeafe5b279a7 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.Opcodes 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 @@ fun isVisibilityPrivateOrPackagePrivate(access: Int): Boolean { 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/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt index 9f3ec4d2b4507d215adda09614ad1589cbdd8408..9bb5381eef06e5dc2fe96c7cb0386d2fbcc19d09 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt @@ -25,9 +25,11 @@ import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.asm.findAnnotationValueAsString import com.android.hoststubgen.asm.findAnyAnnotation +import com.android.hoststubgen.asm.toHumanReadableClassName import com.android.hoststubgen.asm.toHumanReadableMethodName import com.android.hoststubgen.asm.toJvmClassName import com.android.hoststubgen.log +import com.android.hoststubgen.utils.ClassFilter import org.objectweb.asm.tree.AnnotationNode import org.objectweb.asm.tree.ClassNode @@ -51,6 +53,7 @@ class AnnotationBasedFilter( nativeSubstituteAnnotations_: Set<String>, classLoadHookAnnotations_: Set<String>, stubStaticInitializerAnnotations_: Set<String>, + private val annotationAllowedClassesFilter: ClassFilter, fallback: OutputFilter, ) : DelegatingFilter(fallback) { private var stubAnnotations = convertToInternalNames(stubAnnotations_) @@ -62,7 +65,8 @@ class AnnotationBasedFilter( private var substituteAnnotations = convertToInternalNames(substituteAnnotations_) private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_) private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_) - private var stubStaticInitializerAnnotations = convertToInternalNames(stubStaticInitializerAnnotations_) + private var stubStaticInitializerAnnotations = + convertToInternalNames(stubStaticInitializerAnnotations_) /** Annotations that control API visibility. */ private var visibilityAnnotations: Set<String> = convertToInternalNames( @@ -135,15 +139,22 @@ class AnnotationBasedFilter( * name1 - 4 are only used in exception messages. */ private fun findAnnotation( - visibles: List<AnnotationNode>?, - invisibles: List<AnnotationNode>?, - type: String, - name1: String, - name2: String = "", - name3: String = "", + className: String, + visibles: List<AnnotationNode>?, + invisibles: List<AnnotationNode>?, + type: String, + name1: String, + name2: String = "", + name3: String = "", ): FilterPolicyWithReason? { detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3) + if (!annotationAllowedClassesFilter.matches(className)) { + throw InvalidAnnotationException( + "Class ${className.toHumanReadableClassName()} is not allowed to have " + + "Ravenwood annotations. Contact g/ravenwood for more details.") + } + findAnyAnnotation(stubAnnotations, visibles, invisibles)?.let { return FilterPolicy.Stub.withReason(reasonAnnotation) } @@ -170,6 +181,7 @@ class AnnotationBasedFilter( val cn = classes.getClass(className) findAnnotation( + cn.name, cn.visibleAnnotations, cn.invisibleAnnotations, "class", @@ -193,6 +205,7 @@ class AnnotationBasedFilter( cn.fields?.firstOrNull { it.name == fieldName }?.let {fn -> findAnnotation( + cn.name, fn.visibleAnnotations, fn.invisibleAnnotations, "field", @@ -229,6 +242,7 @@ class AnnotationBasedFilter( // If there's no substitution, then we check the annotation. findAnnotation( + cn.name, mn.visibleAnnotations, mn.invisibleAnnotations, "method", 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 07a023c66926dcf78e13b50103da1a6473c864ac..9c0fa69f8a14aade75950c9aeaf9c345c9dbb3a7 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.CLASS_INITIALIZER_NAME 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 @@ class ImplicitOutputFilter( 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 @@ class ImplicitOutputFilter( } } + 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/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index 46546e8b94913abac73c5fd3e71b91e582d7b9d1..416f08505867858311ddcb703ffc8b92f7f52bd6 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -15,7 +15,7 @@ */ package com.android.hoststubgen.filters -import com.android.hoststubgen.UserErrorException +import com.android.hoststubgen.ParseException import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.log import com.android.hoststubgen.normalizeTextLine @@ -46,30 +46,6 @@ private fun isVisible(access: Int): Boolean { return (access and (Opcodes.ACC_PUBLIC or Opcodes.ACC_PROTECTED)) != 0 } -/** - * Exception for a parse error. - */ -private class ParseException : Exception, UserErrorException { - val hasSourceInfo: Boolean - - constructor(message: String) : super(message) { - hasSourceInfo = false - } - - constructor(message: String, file: String, line: Int) : - super("$message in file $file line $line") { - hasSourceInfo = true - } - - fun withSourceInfo(filename: String, lineNo: Int): ParseException { - if (hasSourceInfo) { - return this // Already has source information. - } else { - return ParseException(this.message ?: "", filename, lineNo) - } - } -} - private const val FILTER_REASON = "file-override" /** diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt new file mode 100644 index 0000000000000000000000000000000000000000..01a7ab3eacfa56c2026c05dfc29a42d39230b35a --- /dev/null +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt @@ -0,0 +1,141 @@ +/* + * 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.utils + +import com.android.hoststubgen.ParseException +import com.android.hoststubgen.asm.toHumanReadableClassName +import com.android.hoststubgen.asm.toJvmClassName +import com.android.hoststubgen.normalizeTextLine +import java.io.File + +/** + * General purpose filter for class names. + */ +class ClassFilter private constructor ( + val defaultResult: Boolean, +) { + private data class FilterElement( + val allowed: Boolean, + val internalName: String, + val isPrefix: Boolean, + ) { + fun matches(classInternalName: String): Boolean { + if (isPrefix) { + return classInternalName.startsWith(internalName) + } else { + return classInternalName == internalName + } + } + } + + private val elements: MutableList<FilterElement> = mutableListOf() + + private val cache: MutableMap<String, Boolean> = mutableMapOf() + + /** + * Takes an internal class name (e.g. "com/android/hoststubgen/ClassName") and returns if + * matches the filter or not. + */ + fun matches(classInternalName: String): Boolean { + cache[classInternalName]?.let { + return it + } + + var result = defaultResult + run outer@{ + elements.forEach { e -> + if (e.matches(classInternalName)) { + result = e.allowed + return@outer // break equivalent. + } + } + } + cache[classInternalName] = result + + return result + } + + fun getCacheSizeForTest(): Int { + return cache.size + } + + companion object { + /** + * Return a filter that alawys returns true or false. + */ + fun newNullFilter(defaultResult: Boolean): ClassFilter { + return ClassFilter(defaultResult) + } + + /** Build a filter from a file. */ + fun loadFromFile(filename: String, defaultResult: Boolean): ClassFilter { + return buildFromString(File(filename).readText(), defaultResult, filename) + } + + /** Build a filter from a string (for unit tests). */ + fun buildFromString( + filterString: String, + defaultResult: Boolean, + filenameForErrorMessage: String + ): ClassFilter { + val ret = ClassFilter(defaultResult) + + var lineNo = 0 + filterString.split('\n').forEach { s -> + lineNo++ + + var line = normalizeTextLine(s) + + if (line.isEmpty()) { + return@forEach // skip empty lines. + } + + line = line.toHumanReadableClassName() // Convert all the slashes to periods. + + var allow = true + if (line.startsWith("!")) { + allow = false + line = line.substring(1).trimStart() + } + + // Special case -- matches any class names. + if (line == "*") { + ret.elements.add(FilterElement(allow, "", true)) + return@forEach + } + + // Handle wildcard -- e.g. "package.name.*" + if (line.endsWith(".*")) { + ret.elements.add(FilterElement( + allow, line.substring(0, line.length - 2).toJvmClassName(), true)) + return@forEach + } + + // Any other uses of "*" would be an error. + if (line.contains('*')) { + throw ParseException( + "Wildcard (*) can only show up as the last element", + filenameForErrorMessage, + lineNo + ) + } + ret.elements.add(FilterElement(allow, line.toJvmClassName(), false)) + } + + return ret + } + } +} \ No newline at end of file diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp index 3dc6da348937face5401e7a23254b25f99c6bad4..e7873d6eecc37b6618a3b1a848d2dd9e9234b63d 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp @@ -16,6 +16,7 @@ java_library { static_libs: [ "hoststubgen-annotations", ], + visibility: ["//frameworks/base/tools/hoststubgen:__subpackages__"], } // Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules. @@ -30,6 +31,7 @@ java_genrule_host { ":hoststubgen-test-tiny-framework", "policy-override-tiny-framework.txt", ], + visibility: ["//visibility:private"], } java_genrule_host { @@ -41,6 +43,7 @@ java_genrule_host { out: [ "host_stub.jar", ], + visibility: ["//visibility:private"], } java_genrule_host { @@ -52,6 +55,7 @@ java_genrule_host { out: [ "host_impl.jar", ], + visibility: ["//visibility:private"], } // Same as "hoststubgen-test-tiny-framework-host", but with more options, to test more hoststubgen @@ -71,6 +75,7 @@ java_genrule_host { ":hoststubgen-test-tiny-framework", "policy-override-tiny-framework.txt", ], + visibility: ["//visibility:private"], } java_genrule_host { @@ -82,6 +87,7 @@ java_genrule_host { out: [ "host_stub.jar", ], + visibility: ["//visibility:private"], } java_genrule_host { @@ -93,6 +99,7 @@ java_genrule_host { out: [ "host_impl.jar", ], + visibility: ["//visibility:private"], } // Compile the test jar, using 2 rules. diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd9e85e17890f41ef0a53033deaa27a9e7175626 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt @@ -0,0 +1,29 @@ +# Only classes listed here can use the hoststubgen annotations. + +# For each class, we check each item in this file, and when a match is found, we +# either allow it if the line doesn't have a !, or disallow if the line has a !. +# All the lines after the matching line will be ignored. + + +# To allow a specific class to use annotations: +# com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + +# To disallow a specific class to use annotations: +# !com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations + +# To allow a specific package to use annotations: +# com.android.hoststubgen.test.* + +# To disallow a specific package to use annotations: +# !com.android.hoststubgen.test.* + + +com.android.hoststubgen.test.tinyframework.* +com.supported.* +com.unsupported.* + +# Use this to allow all packages +# * + +# Use this to allow all packages +# !* \ 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 f627c6e7d7b5311777e37db407b58a5eed066bcd..78a4fa692c217ddb4bb939693516167d4b4be771 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 @@ RuntimeInvisibleAnnotations: 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 d7f0149494c0bc70b5d207a4c09f172eae7cdb03..df63815ea0ae7804581fd076dd767f57eeb7bcfa 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 @@ RuntimeInvisibleAnnotations: 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 131e0b1f8b48a135f169d76bc6a96b22ffdb77c8..2218d8d0b2e123635eedc9ddb5240f30ccb71303 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 @@ RuntimeInvisibleAnnotations: 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 d7f0149494c0bc70b5d207a4c09f172eae7cdb03..df63815ea0ae7804581fd076dd767f57eeb7bcfa 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 @@ RuntimeInvisibleAnnotations: 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 3318c7de13f9820521c3aaba81b2116210dd8ef2..3ac9c6a816f15162de3179c221f1353fd141016d 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 @@ RuntimeInvisibleAnnotations: 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/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh index e212890592ff151af005a06d9412fb187b650d92..872bbf878de4083a8e55b247889872afe748516a 100755 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh @@ -93,6 +93,7 @@ run $HOSTSTUBGEN \ --gen-keep-all-file out/tiny-framework_keep_all.txt \ --gen-input-dump-file out/tiny-framework_dump.txt \ --package-redirect com.unsupported:com.supported \ + --annotation-allowed-classes-file annotation-allowed-classes-tiny-framework.txt \ $HOSTSTUBGEN_OPTS # Extract the jar files, so we can look into them. 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 0000000000000000000000000000000000000000..51f48188fe74f7d3ef0f220bf8d23b0f35f87f86 --- /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 0000000000000000000000000000000000000000..f440d8667fb4b86a7d78ab986fcbdda8271f72d7 --- /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 29aabc702809e7f9755febc9691a474802edb6ee..ecb181ba4bbf92ad89b448923188b36a18c204a5 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 class TinyFrameworkClassTest { 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, + } + ); + } } diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..f6515142ccdbbe6c998116f9747d477f7492a699 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.hoststubgen.utils + +import com.android.hoststubgen.ParseException +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.fail +import org.junit.Test + +class ClassFilterTest { + @Test + fun testDefaultTrue() { + val f = ClassFilter.newNullFilter(true) + assertThat(f.matches("a/b/c")).isEqualTo(true) + } + + @Test + fun testDefaultFalse() { + val f = ClassFilter.newNullFilter(false) + assertThat(f.matches("a/b/c")).isEqualTo(false) + } + + @Test + fun testComplex1() { + val f = ClassFilter.buildFromString(""" + # ** this is a comment ** + a.b.c # allow + !a.b.d # disallow + * # allow all + """.trimIndent(), false, "X") + assertThat(f.getCacheSizeForTest()).isEqualTo(0) + + assertThat(f.matches("a/b/c")).isEqualTo(true) + assertThat(f.getCacheSizeForTest()).isEqualTo(1) + + assertThat(f.matches("a/b/d")).isEqualTo(false) + assertThat(f.matches("x")).isEqualTo(true) + + assertThat(f.getCacheSizeForTest()).isEqualTo(3) + + // Make sure the cache is working + assertThat(f.matches("x")).isEqualTo(true) + } + + @Test + fun testComplex2() { + val f = ClassFilter.buildFromString(""" + a.b.c # allow + !a.* # disallow everything else in package "a". + !d.e.f # disallow d.e.f. + + # everything else is allowed by default + """.trimIndent(), true, "X") + assertThat(f.matches("a/b/c")).isEqualTo(true) + assertThat(f.matches("a/x")).isEqualTo(false) + assertThat(f.matches("d/e/f")).isEqualTo(false) + assertThat(f.matches("d/e/f/g")).isEqualTo(true) + assertThat(f.matches("x")).isEqualTo(true) + } + + @Test + fun testBadFilter1() { + try { + ClassFilter.buildFromString(""" + a* + """.trimIndent(), true, "FILENAME") + fail("ParseException didn't happen") + } catch (e: ParseException) { + assertThat(e.message).contains("Wildcard") + assertThat(e.message).contains("FILENAME") + assertThat(e.message).contains("line 1") + } + } +} \ No newline at end of file diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh index ba1d404eba68616f72d52c93eae76993a696a9b9..4afa2d7a659a886196eedc6f8e2e870d5ce5da01 100755 --- a/tools/hoststubgen/scripts/run-all-tests.sh +++ b/tools/hoststubgen/scripts/run-all-tests.sh @@ -33,6 +33,9 @@ MUST_BUILD_MODULES=( # First, build all the test / etc modules. This shouldn't fail. run m "${MUST_BUILD_MODULES[@]}" +# Run the hoststubgen unittests / etc +run atest hoststubgentest hoststubgen-invoke-test + # Next, run the golden check. This should always pass too. # The following scripts _should_ pass too, but they depend on the internal paths to soong generated # files, and they may fail when something changes in the build system.