diff options
217 files changed, 5876 insertions, 1485 deletions
diff --git a/Ravenwood.bp b/Ravenwood.bp index 1582266203c6..3310898ccfb5 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 5fc77451e832..e08200b055d8 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 95f901c4a365..b8397d2cd1b4 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 ee9c464219d9..2d235331a672 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 14869dba9646..323f9e9cd7c6 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 1e6aa498f417..483d2a4d26b2 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/system-current.txt b/core/api/system-current.txt index b5e940734b1e..eefa36eb4f2d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10228,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 559db514dabf..3a91e25de9e6 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 a99dfa605407..3d5444997107 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 a3de8faa1273..43023fe9c2ab 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/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index c2bc974a42ae..4b2cee698df2 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -235,12 +235,6 @@ public final class AttributionSource implements Parcelable { } /** @hide */ - public AttributionSource withUid(int uid) { - return new AttributionSource(uid, getPid(), getPackageName(), getAttributionTag(), - getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext()); - } - - /** @hide */ public AttributionSource withPid(int pid) { return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(), getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext()); diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig index 6b78d05c848d..5c5083a691be 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 dbb312720373..19ba6a1d22ed 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/os/Binder.java b/core/java/android/os/Binder.java index 3db1cb0500f9..995622004fa6 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 352c9d2ba81f..86628d95b25a 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); @@ -2957,6 +2957,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; if (e instanceof Parcelable @@ -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/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 1b05982a7569..492e2ac7cc28 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/jni/Android.bp b/core/jni/Android.bp index 3795fc8594ae..440a33244a62 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 91e3c921afe6..66bda6100b31 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 30e546cc290d..82570be8e329 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 139075907bf3..4bde87f65326 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 4b563d7f3bd8..7c9dae0ef306 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 6440cc3952f8..3413ededb889 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 9bd07007586c..47b4a469aa5b 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 a9db91be1d5b..e554b44233b5 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 4d8dac1daaf0..3539476b8ce8 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 27c4cd4d2cd1..95bf49fe501e 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 7ab6e8f27fb2..7071cf2336d6 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 c64c21244fe0..8d3da7b81f5b 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 41c65aec3144..fef8ad7499f7 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 98335988459a..a800e6ea60e4 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 55995df299b0..bc69d1e67668 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 d4f6e1868695..0c39a6928391 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 098a4d868269..cf70c90af8be 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 dfae68429f6d..be9013bec08e 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 1f29735b93a4..dab47e9a9e28 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 9c1bea779201..7c5885adb220 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 2b5b8f7a108e..87ab4969040e 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/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt index 8e76fd22f99a..48c0a2de8f86 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/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index 0890861596a5..e63bbc07bc41 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/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 774478669058..c5ffbb70213e 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 f0461bef170c..098a74655831 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 86096d5bd01c..12cb69da772b 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 284cde537ec0..e2ea57d32cd5 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 9e3bb79f03f3..56b52dc2abe7 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 6c2cb9d71c55..9c879d5a58d1 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 22c586248705..e706eb083ab6 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 dbef7fbd51b2..b92ebb3cdf71 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 1a9ebbc56d68..d60712674990 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 08a8d897c96e..2769dbc430ba 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/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index f0741061a051..f03263b71138 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 a93524f6d873..51164e836590 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 29846ac30909..a88a9c72e4a9 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 c2fc0d9a4d91..6ff36d43f91f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -235,14 +235,18 @@ public final class DeviceConfigService extends Binder { for (String aconfigFlag : aconfigFlagNames) { String val = allFlags.get(aconfigFlag); if (val != null) { + // 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; } - String aconfigFlagNameByPackage = aconfigFlag.substring(idx+1); + + // we intend to print out [package].[flag_name] [namespace]=val + String aconfigFlagNameByPackage = aconfigFlag.substring(idx + 1); String namespace = aconfigFlag.substring(0, idx); - lines.add(aconfigFlagNameByPackage + " " + namespace + "=" + val); + 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 d668c691ee0d..defaa20a84c7 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 77b844debb42..142978284cfb 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 @@ -25,6 +25,7 @@ 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.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan @@ -48,6 +49,8 @@ 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.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 @@ -73,6 +76,7 @@ fun CommunalHub( CommunalContent( modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth), model = communalContent[index], + viewModel = viewModel, deleteOnClick = viewModel::onDeleteWidget, size = SizeF( @@ -94,6 +98,7 @@ fun CommunalHub( @Composable private fun CommunalContent( model: CommunalContentModel, + viewModel: CommunalViewModel, size: SizeF, deleteOnClick: (id: Int) -> Unit, modifier: Modifier = Modifier, @@ -102,6 +107,7 @@ private fun CommunalContent( 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) } } @@ -155,6 +161,31 @@ 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 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 000000000000..c2117ae5bda4 --- /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 000000000000..fde3ad723d71 --- /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/tests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt index 020903057eb3..262795fe0eb6 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 9a3c6d5b322f..8780f58743cd 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 5de370f22eb8..77338410642c 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 000000000000..bba00506df85 --- /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 000000000000..f36ef6630a48 --- /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 000000000000..e398c930e86e --- /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 000000000000..7f1730cb2abd --- /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 ef0609a99e05..0f7772454b78 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 e44927432719..7fa762a6614f 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 c3421de8f663..273adcf83271 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 000000000000..e41c32261c11 --- /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 000000000000..2c6d9e4c39c3 --- /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/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 524cccf4017d..eb36b19972f9 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 @@ -19,6 +19,7 @@ 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 @@ -37,12 +38,14 @@ 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, @@ -87,8 +90,8 @@ constructor( if (isTutorialMode) { return@flatMapLatest flowOf(tutorialContent) } - combine(smartspaceContent, widgetContent) { smartspace, widgets -> - smartspace + widgets + combine(smartspaceContent, umoContent, widgetContent) { smartspace, umo, widgets -> + smartspace + umo + widgets } } @@ -138,4 +141,13 @@ constructor( 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/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt index 69382a5a5552..bb9b4b5f522f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt @@ -53,4 +53,15 @@ sealed interface CommunalContentModel { ) : 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/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 3df6e7e781b6..5efe6ceeb65a 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 @@ -21,7 +21,10 @@ import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.model.CommunalSceneKey import com.android.systemui.dagger.SysUISingleton +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 @@ -30,6 +33,7 @@ class CommunalViewModel @Inject constructor( private val communalInteractor: CommunalInteractor, + @Named(MediaModule.COMMUNAL_HUB) val mediaHost: MediaHost, ) { val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene fun onSceneChanged(scene: CommunalSceneKey) { diff --git a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java index b98794ef8026..9bb23d8564e4 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 58dcf0691687..b34b4599cf70 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 cf868856c419..20a9e5d572c9 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 c9748f954670..0e333f21dd14 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 1e9be09bc3f3..e16bb0bb8482 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 654f2d106206..f5f5571dbb1b 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/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt index 83a6e58cc444..773c292befcf 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 index eea369f15a16..654fffe89471 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 eacfa578ac44..039372d87835 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 index cff22b0dc019..8453af19d26f 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 c77f3f49a77c..fa03dc245745 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 934f31040ce1..82420875e0de 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 05f125f6f531..bff0b93dccb0 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 index f74234bc2e21..e57a0fddc936 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/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt index 65b798a14e0b..62c3e9e486b4 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/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt index 13c99647f00b..9471574fc755 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt @@ -100,6 +100,17 @@ 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 87dd17ec4865..8a8e33efbcef 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 2ecd435177ee..2b3c6326032c 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 cf7bf86504fb..2a510e41ec9c 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 3b300249aac2..b1b6014bfbde 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 4642f552615a..4b0e5d188ffa 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 78fb7a491534..3ed05aac3e10 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 000000000000..e7a91e505e47 --- /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 000000000000..658f0404aa84 --- /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 c61b11ae7336..9a95b17fc7d1 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 a04919185350..44c57f34fa1b 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 2c97809bf367..c159b66c10a0 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 b75f3e0876ff..2cc8f0a47955 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 ba8dcef683c7..390742031381 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 bfaa6edefdca..47db4f8faeca 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 7873899f2d35..3ddac7e1ad1d 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 000000000000..455f9865edf3 --- /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 5460a1b8ce9f..08d54c001d11 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 @@ -24,6 +24,7 @@ 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 @@ -58,6 +59,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var tutorialRepository: FakeCommunalTutorialRepository private lateinit var communalRepository: FakeCommunalRepository + private lateinit var mediaRepository: FakeCommunalMediaRepository private lateinit var widgetRepository: FakeCommunalWidgetRepository private lateinit var smartspaceRepository: FakeSmartspaceRepository private lateinit var keyguardRepository: FakeKeyguardRepository @@ -74,6 +76,7 @@ class CommunalInteractorTest : SysuiTestCase() { tutorialRepository = withDeps.tutorialRepository communalRepository = withDeps.communalRepository + mediaRepository = withDeps.mediaRepository widgetRepository = withDeps.widgetRepository smartspaceRepository = withDeps.smartspaceRepository keyguardRepository = withDeps.keyguardRepository @@ -237,6 +240,66 @@ class CommunalInteractorTest : SysuiTestCase() { } @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 { var desiredScene = collectLastValue(underTest.desiredScene) 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 40f0ed3626db..288f3b651a3c 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 0db3de2ce0dd..1f18705edfdb 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 365f67b5e566..6d5cd49b8af6 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 8b572eb3d906..000000000000 --- 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 90fd6523ec12..4587ea6dbdc8 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() @@ -170,11 +171,34 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { } @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/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt index 7ad2ce8ae110..f4293f035cd1 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/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt index 2d3dc585ac70..f93d52b2c35c 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 f566efe6f176..f3b114d662b5 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 3feb5bfd088a..e84d274b4763 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 49049bc51460..1b4ba6433ded 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 index fd381392c3e9..c848287aa53e 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 74b2723e870e..cce038f4ffc1 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 ac75d4f189cc..03f300542a6f 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) @@ -1179,6 +1181,16 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { } @Test + fun getIsAnySimSecure_delegatesCallToKeyguardUpdateMonitor() = + testScope.runTest { + assertThat(underTest.getIsAnySimSecure()).isFalse() + + whenever(updateMonitor.isSimPinSecure).thenReturn(true) + + assertThat(underTest.getIsAnySimSecure()).isTrue() + } + + @Test fun noSubscriptionsInEcmMode_notInEcmMode() = testScope.runTest { whenever(telephonyManager.emergencyCallbackMode).thenReturn(false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java index 58d93c98f015..2f79955c4f5b 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 1968d75a7964..017eefe38f2a 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 8353cf78d983..d0fa27e3b79a 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 000000000000..3ab1b6c11e02 --- /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/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt index 6c3882fd70f6..0c821eab65e0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt @@ -18,6 +18,7 @@ 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 @@ -35,6 +36,7 @@ object CommunalInteractorFactory { testScope: TestScope = TestScope(), communalRepository: FakeCommunalRepository = FakeCommunalRepository(), widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(), + mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(), smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(), tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(), appWidgetHost: AppWidgetHost = mock(), @@ -48,6 +50,7 @@ object CommunalInteractorFactory { return WithDependencies( communalRepository, widgetRepository, + mediaRepository, smartspaceRepository, tutorialRepository, withDeps.keyguardRepository, @@ -57,6 +60,7 @@ object CommunalInteractorFactory { CommunalInteractor( communalRepository, widgetRepository, + mediaRepository, smartspaceRepository, withDeps.communalTutorialInteractor, appWidgetHost, @@ -67,6 +71,7 @@ object CommunalInteractorFactory { data class WithDependencies( val communalRepository: FakeCommunalRepository, val widgetRepository: FakeCommunalWidgetRepository, + val mediaRepository: FakeCommunalMediaRepository, val smartspaceRepository: FakeSmartspaceRepository, val tutorialRepository: FakeCommunalTutorialRepository, val keyguardRepository: FakeKeyguardRepository, 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 957fbbd8680f..ace650020a9d 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 14c4b8670060..36ec18fc4de4 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 @@ -41,6 +49,7 @@ 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,12 +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() } 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) @@ -183,7 +206,7 @@ class SceneTestUtils( featureFlags = featureFlags, sceneContainerFlags = sceneContainerFlags, bouncerRepository = FakeKeyguardBouncerRepository(), - configurationRepository = FakeConfigurationRepository(), + configurationRepository = configurationRepository, shadeRepository = FakeShadeRepository(), sceneInteractorProvider = { sceneInteractor() }, powerInteractor = PowerInteractorFactory.create().powerInteractor, @@ -217,6 +240,7 @@ class SceneTestUtils( fun bouncerViewModel( bouncerInteractor: BouncerInteractor, authenticationInteractor: AuthenticationInteractor, + actionButtonInteractor: BouncerActionButtonInteractor, users: List<UserViewModel> = createUsers(), ): BouncerViewModel { return BouncerViewModel( @@ -229,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 } } @@ -285,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/telephony/data/repository/FakeTelephonyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/FakeTelephonyRepository.kt index 992ac62fe3c4..5cde3868199b 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 000000000000..f0a8fd0abf3d --- /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 d0e442e7058d..5c9bf1828347 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 000000000000..0aac084dd4ce --- /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 000000000000..a6b3f668efa6 --- /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 b37bbd6ea27f..ab678d93c665 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/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 02235096ac15..15fc2dc15d02 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 afc0dd188c3c..cd9c53bb92b9 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 4bdb4da97144..5f1a7e7e8123 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 a0a5f99c1152..b99a98fe6e8b 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; @@ -5896,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 @@ -20121,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 @@ -20160,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); } @@ -20214,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 947fcd38f5a0..18a91535a34c 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 d6997daaa12b..052b0c2078d1 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/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java index b006ac862533..a8cba532435d 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 26e70c0279e3..21284a05ddad 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 5b32a94c4eac..a5bc2c36a5a8 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 9bca9f0192b2..36677df07ca3 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/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/CrossProfileAppsService.java index 486282aa2c04..0bd165ec91fa 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 5f28e56e996c..f1dc2849a391 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 72f3afc35e99..3313e7234a19 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 51214faca730..56e9c0a3544b 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 a8da818932fa..1d38bbb2a1bd 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 29322e2553e9..888e1c26206c 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 dc5915de5551..db05dd394915 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 dd96a2b84a97..cc6bb007411e 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 be4fb5c539a9..fc831205f60e 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 ca8dc2973404..187cadaf4d46 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 8b82a1c6e7c9..3f4cc4a84ffd 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 a4d8632ac077..98eee4dc3b1d 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 1be28ca9e915..4b466be8476b 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/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java index 9428ef6c8ba9..655b9c93d9dd 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 d4abad8499f2..6f45d2befc6b 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 a09e71311125..c829e1c79ba8 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/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 5507e7c7754a..18caafdaa56a 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 cf5aa7b1eb02..e667bfe36d18 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 440823c43607..6338965e4a80 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 8667888fbb51..12115afcbeec 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 c6aba2ab9cbe..3adeb4b5925f 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 e04a1e5b3569..31541d083fbc 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 895edce093b5..651578dc5deb 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 6e738daf9315..78c13f854fe4 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 d3a64bb0b9ac..2ab7db47f8e5 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 9e20805c2874..c26cf1c9a113 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 d82a5000844f..3b10c7f2c2a6 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 056aae49ae82..75dd67d97620 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 d6a7dc6681b9..6f6bb45bad50 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 70986c3cfbac..4c831d36c9e3 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 8240c47a607b..061698a929d6 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 fac681aaf1c4..0ceda421913d 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 9115775acd05..80cde73ecf1f 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 11f62e953525..3d4d4eca7f48 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 6c56360177d8..d71dbbb5d53b 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 c2821aef2142..b83421fe78d7 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/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java index a388932cb708..b8e581f32a58 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/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 08af75c10d38..c0bf2ce41435 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(); diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp index dc05462c1a1c..80427b346f1a 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 6026a107c67f..2ef94ac4a3b0 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 37cf8445920f..cdd926622dcc 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 7ceccc57c0f1..47ae97fc5d27 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 032d026648df..2f909f818bfe 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 dcbee83b839b..bb91939c430e 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 daed5df4edd7..646f4862d75d 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/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java new file mode 100644 index 000000000000..f537efd9736d --- /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 01a91c1db1e6..398148ff4d3b 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/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index c4d03bebc751..49a888689e60 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/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 2689e9d14c00..a83caa4a4e95 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 f61cce666cca..daaab3314679 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/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING index 970362611f53..e02492d68bb4 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 182940e6f7c5..fd4ec8bd5931 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 d749f076f3b5..12c7841556fc 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 000000000000..7e90e421a1f9 --- /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 000000000000..34b2145b8d22 --- /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 872d56856c33..f32dc721873e 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 d74612d48de2..aab02b8de254 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 f75062b3a878..937e56c2cbb5 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 a51bdcf0c793..1bcf3642082f 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 9f3ec4d2b450..9bb5381eef06 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 07a023c66926..9c0fa69f8a14 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 46546e8b9491..416f08505867 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 000000000000..01a7ab3eacfa --- /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 3dc6da348937..e7873d6eecc3 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 000000000000..bd9e85e17890 --- /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 f627c6e7d7b5..78a4fa692c21 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 d7f0149494c0..df63815ea0ae 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 131e0b1f8b48..2218d8d0b2e1 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 d7f0149494c0..df63815ea0ae 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 3318c7de13f9..3ac9c6a816f1 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 e212890592ff..872bbf878de4 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 000000000000..51f48188fe74 --- /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 000000000000..f440d8667fb4 --- /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 29aabc702809..ecb181ba4bbf 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 000000000000..f6515142ccdb --- /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 ba1d404eba68..4afa2d7a659a 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. |