Merge "[flexiglass] Password bouncer isn't side-by-side on unfolded." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 14869db..323f9e9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4464,6 +4464,7 @@
method public boolean onPreparePanel(int, @Nullable android.view.View, @NonNull android.view.Menu);
method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]);
method @FlaggedApi("android.permission.flags.device_aware_permission_apis") public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[], int);
@@ -13973,6 +13974,7 @@
method public String getColumnName(int);
method public android.os.Bundle getExtras();
method public android.net.Uri getNotificationUri();
+ method public java.util.List<android.net.Uri> getNotificationUris();
method public final int getPosition();
method public int getType(int);
method @Deprecated protected Object getUpdatedField(int);
@@ -13998,6 +14000,7 @@
method public android.os.Bundle respond(android.os.Bundle);
method public void setExtras(android.os.Bundle);
method public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method public void setNotificationUris(@NonNull android.content.ContentResolver, @NonNull java.util.List<android.net.Uri>);
method public void unregisterContentObserver(android.database.ContentObserver);
method public void unregisterDataSetObserver(android.database.DataSetObserver);
field @Deprecated protected boolean mClosed;
@@ -14129,6 +14132,7 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
+ method public void remove();
}
public enum CursorJoiner.Result {
@@ -14196,6 +14200,7 @@
method public int getInt(int);
method public long getLong(int);
method public android.net.Uri getNotificationUri();
+ method public java.util.List<android.net.Uri> getNotificationUris();
method public int getPosition();
method public short getShort(int);
method public String getString(int);
@@ -14220,6 +14225,7 @@
method public android.os.Bundle respond(android.os.Bundle);
method public void setExtras(android.os.Bundle);
method public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>);
method public void unregisterContentObserver(android.database.ContentObserver);
method public void unregisterDataSetObserver(android.database.DataSetObserver);
}
@@ -22213,6 +22219,9 @@
ctor public MediaCodec.CryptoException(int, @Nullable String);
method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo();
method public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
field @Deprecated public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
@@ -22725,6 +22734,9 @@
public final class MediaCryptoException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaCryptoException(@Nullable String);
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
}
public abstract class MediaDataSource implements java.io.Closeable {
@@ -22949,6 +22961,9 @@
public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
method @NonNull public String getDiagnosticInfo();
method public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
method public boolean isTransient();
}
@@ -23022,6 +23037,9 @@
public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
method public boolean isTransient();
field @Deprecated public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0
@@ -23029,6 +23047,9 @@
public class MediaDrmException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaDrmException(String);
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
}
public class MediaDrmResetException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
@@ -31918,6 +31939,7 @@
method public void surfaceCreated(android.view.SurfaceHolder);
method public void surfaceDestroyed(android.view.SurfaceHolder);
method @Deprecated public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public void surfaceRedrawNeededAsync(android.view.SurfaceHolder, Runnable);
field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -47459,6 +47481,7 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public String next();
+ method public void remove();
method public void setString(String);
}
@@ -48924,6 +48947,7 @@
method public void ensureCapacity(int);
method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
method public boolean equals(@Nullable Object);
+ method public void forEach(java.util.function.BiConsumer<? super K,? super V>);
method public V get(Object);
method public int hashCode();
method public int indexOfKey(Object);
@@ -48937,6 +48961,7 @@
method public V remove(Object);
method public boolean removeAll(java.util.Collection<?>);
method public V removeAt(int);
+ method public void replaceAll(java.util.function.BiFunction<? super K,? super V,? extends V>);
method public boolean retainAll(java.util.Collection<?>);
method public V setValueAt(int, V);
method public int size();
@@ -48967,6 +48992,7 @@
method public boolean removeAll(android.util.ArraySet<? extends E>);
method public boolean removeAll(java.util.Collection<?>);
method public E removeAt(int);
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public boolean retainAll(java.util.Collection<?>);
method public int size();
method public Object[] toArray();
@@ -52834,6 +52860,7 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(@NonNull android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
+ method @Nullable public android.window.OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(@NonNull android.view.View, @NonNull android.view.View);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams();
@@ -52875,6 +52902,7 @@
method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect);
method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect);
+ method @CallSuper public void onDescendantInvalidated(@NonNull android.view.View, @NonNull android.view.View);
method public boolean onInterceptHoverEvent(android.view.MotionEvent);
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
@@ -55058,12 +55086,14 @@
method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method @Nullable public android.os.Handler getHandler();
method @Nullable public CharSequence getSelectedText(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public static final void removeComposingSpans(@NonNull android.text.Spannable);
+ method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
@@ -55071,6 +55101,7 @@
method public static void setComposingSpans(@NonNull android.text.Spannable);
method public boolean setComposingText(CharSequence, int);
method public boolean setSelection(int, int);
+ method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot();
}
public final class CompletionInfo implements android.os.Parcelable {
@@ -55407,6 +55438,7 @@
method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(CharSequence, int);
+ method public boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean deleteSurroundingText(int, int);
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
@@ -55415,18 +55447,29 @@
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(int, int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
+ method public void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
+ method public boolean performSpellCheck();
+ method public boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal);
+ method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
+ method public boolean requestCursorUpdates(int, int);
+ method public void requestTextBoundsInfo(@NonNull android.graphics.RectF, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inputmethod.TextBoundsInfoResult>);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
+ method public boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean setComposingText(CharSequence, int);
+ method public boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
+ method public boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
method public void setTarget(android.view.inputmethod.InputConnection);
+ method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot();
}
public final class InputContentInfo implements android.os.Parcelable {
@@ -57993,6 +58036,7 @@
public abstract class BaseAdapter implements android.widget.ListAdapter android.widget.SpinnerAdapter {
ctor public BaseAdapter();
method public boolean areAllItemsEnabled();
+ method public CharSequence[] getAutofillOptions();
method public android.view.View getDropDownView(int, android.view.View, android.view.ViewGroup);
method public int getItemViewType(int);
method public int getViewTypeCount();
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1e6aa49..483d2a4 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 b5e9407..eefa36e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10228,6 +10228,7 @@
ctor public ParcelableHolder(int);
method public int describeContents();
method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>);
+ method public int getStability();
method public void readFromParcel(@NonNull android.os.Parcel);
method public void setParcelable(@Nullable android.os.Parcelable);
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 559db51..3a91e25 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/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 6b78d05..5c5083a 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 @@
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/jni/Android.bp b/core/jni/Android.bp
index 3795fc8..440a332 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -43,7 +43,10 @@
"-Wall",
"-Werror",
+ "-Wextra",
+ "-Wthread-safety",
"-Wno-error=deprecated-declarations",
+ "-Wno-unused-parameter",
"-Wunused",
"-Wunreachable-code",
diff --git a/core/jni/android_content_res_ResourceTimer.cpp b/core/jni/android_content_res_ResourceTimer.cpp
index 91e3c921..66bda61 100644
--- a/core/jni/android_content_res_ResourceTimer.cpp
+++ b/core/jni/android_content_res_ResourceTimer.cpp
@@ -44,9 +44,9 @@
static int NativeGetTimers(JNIEnv* env, jobject /*clazz*/, jobjectArray timer, jboolean reset) {
size_t size = ResourceTimer::counterSize;
- if (jsize st = env->GetArrayLength(timer); st < size) {
- // Shrink the size to the minimum of the available counters and the available space.
- size = st;
+ if (size_t st = env->GetArrayLength(timer); st < size) {
+ // Shrink the size to the minimum of the available counters and the available space.
+ size = st;
}
for (size_t i = 0; i < size; i++) {
ResourceTimer::Timer src;
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 30e546c..82570be8 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1892,8 +1892,8 @@
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_NOISE_PROFILE);
- const status_t numPlaneColors = isBayer ? 3 : 1;
- const status_t numCfaChannels = isBayer ? 4 : 1;
+ const unsigned long numPlaneColors = isBayer ? 3 : 1;
+ const unsigned long numCfaChannels = isBayer ? 4 : 1;
uint8_t cfaOut[numCfaChannels];
if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
diff --git a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
index 1390759..4bde87f 100644
--- a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
+++ b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp
@@ -599,7 +599,7 @@
quality, cropLeft, cropTop, cropRight, cropBottom, rot90);
size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob);
- if (finalJpegSize > outBufCapacity) {
+ if (finalJpegSize > static_cast<size_t>(outBufCapacity)) {
ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\
"capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity);
return actualJpegSize;
diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp
index 4b563d7..7c9dae0 100644
--- a/core/jni/android_media_AudioProductStrategies.cpp
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -88,12 +88,12 @@
int attrGroupIndex = 0;
std::map<int /**attributesGroupIndex*/, std::vector<VolumeGroupAttributes> > groups;
for (const auto &attr : strategy.getVolumeGroupAttributes()) {
- int groupId = attr.getGroupId();
+ auto groupId = attr.getGroupId();
int streamType = attr.getStreamType();
const auto &iter = std::find_if(begin(groups), end(groups),
[groupId, streamType](const auto &iter) {
const auto &frontAttr = iter.second.front();
- return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType;
+ return (frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType);
});
// Same Volume Group Id and same stream type
if (iter != end(groups)) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 6440cc3..3413ede 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2501,7 +2501,7 @@
int *nativeArray = nullptr;
nativeArray = env->GetIntArrayElements(jArray, 0);
if (nativeArray != nullptr) {
- for (size_t i = 0; i < len; i++) {
+ for (size_t i = 0; i < static_cast<size_t>(len); i++) {
nativeVector.push_back(nativeArray[i]);
}
env->ReleaseIntArrayElements(jArray, nativeArray, 0);
@@ -2565,7 +2565,7 @@
if (nativeSystemUsages != nullptr) {
jsize len = env->GetArrayLength(systemUsages);
- for (size_t i = 0; i < len; i++) {
+ for (size_t i = 0; i < static_cast<size_t>(len); i++) {
audio_usage_t nativeAudioUsage =
static_cast<audio_usage_t>(nativeSystemUsages[i]);
nativeSystemUsagesVector.push_back(nativeAudioUsage);
@@ -2776,7 +2776,7 @@
return jStatus;
}
- if (devices.size() > maxResultSize) {
+ if (devices.size() > static_cast<size_t>(maxResultSize)) {
return AUDIO_JAVA_INVALID_OPERATION;
}
size_t index = 0;
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 9bd0700..47b4a46 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -221,7 +221,7 @@
ssize_t rc = SendFileDescriptorVector(fd, buffer, len, fds);
- while (rc != len) {
+ while (rc != static_cast<ssize_t>(len)) {
if (rc == -1) {
jniThrowIOException(env, errno);
return -1;
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index a9db91b..e554b44 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -265,7 +265,7 @@
// justify passing such a large amount of data over this path. So the
// alternative (updating the constructor and other code to accept other
// types, should also probably not be taken in this case).
- CHECK_LE(size, std::numeric_limits<jint>::max());
+ CHECK_LE(size, static_cast<size_t>(std::numeric_limits<jint>::max()));
return env->NewObject(clazz.get(), constructID, static_cast<jint>(size));
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 4d8dac1..3539476 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -691,7 +691,7 @@
// String tries to allocate itself on the stack, within a known size, but will
// make a heap allocation if not.
-template <size_t StackReserve>
+template <jsize StackReserve>
class StackString {
public:
StackString(JNIEnv* env, jstring str) : mEnv(env), mJStr(str) {
diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp
index 27c4cd4..95bf49f 100644
--- a/core/jni/android_os_PerformanceHintManager.cpp
+++ b/core/jni/android_os_PerformanceHintManager.cpp
@@ -224,7 +224,7 @@
return nullptr;
}
jint* threadIds = env->GetIntArrayElements(jintArr, 0);
- for (int i = 0; i < size; ++i) {
+ for (size_t i = 0; i < size; ++i) {
threadIds[i] = tidsVector[i];
}
env->ReleaseIntArrayElements(jintArr, threadIds, 0);
diff --git a/core/jni/android_util_CharsetUtils.cpp b/core/jni/android_util_CharsetUtils.cpp
index 7ab6e8f2..7071cf2 100644
--- a/core/jni/android_util_CharsetUtils.cpp
+++ b/core/jni/android_util_CharsetUtils.cpp
@@ -25,7 +25,7 @@
// Quickly check if destination has plenty of room for worst-case
// 4-bytes-per-char encoded size
- const size_t worstLen = (srcLen * 4);
+ const jint worstLen = (srcLen * 4);
if (destOff >= 0 && destOff + worstLen < destLen) {
env->GetStringUTFRegion(src, 0, srcLen, destPtr + destOff);
return strlen(destPtr + destOff + srcLen) + srcLen;
@@ -33,7 +33,7 @@
// String still might fit in destination, but we need to measure
// its actual encoded size to be sure
- const size_t encodedLen = env->GetStringUTFLength(src);
+ const jint encodedLen = env->GetStringUTFLength(src);
if (destOff >= 0 && destOff + encodedLen < destLen) {
env->GetStringUTFRegion(src, 0, srcLen, destPtr + destOff);
return encodedLen;
diff --git a/core/jni/android_util_FileObserver.cpp b/core/jni/android_util_FileObserver.cpp
index c64c212..8d3da7b 100644
--- a/core/jni/android_util_FileObserver.cpp
+++ b/core/jni/android_util_FileObserver.cpp
@@ -114,7 +114,7 @@
if (fd >= 0)
{
size_t count = wfds.size();
- for (jsize i = 0; i < count; ++i) {
+ for (size_t i = 0; i < count; ++i) {
jstring pathString = (jstring) env->GetObjectArrayElement(pathStrings, i);
ScopedUtfChars path(env, pathString);
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 41c65ae..fef8ad7 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -140,7 +140,7 @@
env->ExceptionClear();
return NULL;
}
- for (int i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
VsyncEventData::FrameTimeline frameTimeline = vsyncEventData.frameTimelines[i];
ScopedLocalRef<jobject>
frameTimelineObj(env,
@@ -193,7 +193,7 @@
gDisplayEventReceiverClassInfo
.vsyncEventDataClassInfo
.frameTimelines)));
- for (int i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
+ for (size_t i = 0; i < vsyncEventData.frameTimelinesLength; i++) {
VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
ScopedLocalRef<jobject>
frameTimelineObj(env, env->GetObjectArrayElement(frameTimelinesObj.get(), i));
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9833598..a800e6e 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1950,7 +1950,7 @@
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
- for (int i = 0; i < jankData.size(); i++) {
+ for (size_t i = 0; i < jankData.size(); i++) {
jobject jJankData = env->NewObject(gJankDataClassInfo.clazz,
gJankDataClassInfo.ctor, jankData[i].frameVsyncId, jankData[i].jankType);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index 55995df..bc69d1e6 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -67,7 +67,7 @@
static jobjectArray fromWindowInfos(JNIEnv* env, const std::vector<WindowInfo>& windowInfos) {
jobjectArray jWindowHandlesArray =
env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
- for (int i = 0; i < windowInfos.size(); i++) {
+ for (size_t i = 0; i < windowInfos.size(); i++) {
ScopedLocalRef<jobject>
jWindowHandle(env,
android_view_InputWindowHandle_fromWindowInfo(env, windowInfos[i]));
@@ -80,7 +80,7 @@
static jobjectArray fromDisplayInfos(JNIEnv* env, const std::vector<DisplayInfo>& displayInfos) {
jobjectArray jDisplayInfoArray =
env->NewObjectArray(displayInfos.size(), gDisplayInfoClassInfo.clazz, nullptr);
- for (int i = 0; i < displayInfos.size(); i++) {
+ for (size_t i = 0; i < displayInfos.size(); i++) {
ScopedLocalRef<jobject> jDisplayInfo(env, fromDisplayInfo(env, displayInfos[i]));
env->SetObjectArrayElement(jDisplayInfoArray, i, jDisplayInfo.get());
}
diff --git a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp
index d4f6e18..0c39a69 100644
--- a/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp
+++ b/core/jni/com_android_internal_content_om_OverlayManagerImpl.cpp
@@ -266,11 +266,11 @@
auto jsResourceName = reinterpret_cast<jstring>(
env->GetObjectField(entry, gFabricatedOverlayInternalEntryOffsets.resourceName));
const ScopedUtfChars resourceName(env, jsResourceName);
- const auto dataType =
+ const jint dataType =
env->GetIntField(entry, gFabricatedOverlayInternalEntryOffsets.dataType);
// In Java, the data type is int but the maximum value of data Type is less than 0xff.
- if (dataType >= UCHAR_MAX) {
+ if (dataType >= static_cast<jint>(UCHAR_MAX)) {
jniThrowException(env, IllegalArgumentException, "Unsupported data type");
return;
}
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
index 098a4d8..cf70c90 100644
--- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -55,7 +55,7 @@
static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
jint endUid) {
- for (uint32_t uid = startUid; uid <= endUid; ++uid) {
+ for (jint uid = startUid; uid <= endUid; ++uid) {
if (!android::bpf::clearUidTimes(uid)) return false;
}
return true;
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
index dfae684..be9013b 100644
--- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -147,7 +147,7 @@
std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
- for (int i = 0; i < selectedThreadIds.size(); i++) {
+ for (size_t i = 0; i < selectedThreadIds.size(); i++) {
if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i],
SELECTED_THREAD_AGGREGATION_KEY)) {
return false;
@@ -312,11 +312,11 @@
auto fields = android::base::Split(line.c_str(), ":");
android::base::ParseUint(fields[0], &aggregationKey);
- for (int j = 1; j < fields.size(); j++) {
+ for (size_t j = 1; j < fields.size(); j++) {
auto numbers = android::base::Split(fields[j], " ");
std::vector<uint64_t> chunk;
- for (int k = 0; k < numbers.size(); k++) {
+ for (size_t k = 0; k < numbers.size(); k++) {
uint64_t time;
android::base::ParseUint(numbers[k], &time);
chunk.emplace_back(time);
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 1f29735..dab47e9 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -288,7 +288,7 @@
}
bool nonZero = false;
- for (int i = 0; i < vector->size(); i++) {
+ for (size_t i = 0; i < vector->size(); i++) {
jint index = scopedIndexMap[i];
if (index < 0 || index >= size) {
jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9c1bea7..7c5885a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1094,8 +1094,8 @@
}
struct dirent* ent;
while ((ent = readdir(dir.get()))) {
- if (ent->d_ino == ce_data_inode) {
- return ent->d_name;
+ if (static_cast<long long>(ent->d_ino) == ce_data_inode) {
+ return ent->d_name;
}
}
}
@@ -1765,14 +1765,14 @@
static void ReloadBuildJavaConstants(JNIEnv* env) {
jclass build_cls = env->FindClass("android/os/Build");
size_t arr_size = sizeof(build_constants) / sizeof(build_constants[0]);
- for (int i = 0; i < arr_size; i++) {
+ for (size_t i = 0; i < arr_size; i++) {
const char* field_name = build_constants[i].first;
const char* sysprop_name = build_constants[i].second;
ReloadBuildJavaConstant(env, build_cls, field_name, "Ljava/lang/String;", sysprop_name);
}
jclass build_version_cls = env->FindClass("android/os/Build$VERSION");
arr_size = sizeof(build_version_constants) / sizeof(build_version_constants[0]);
- for (int i = 0; i < arr_size; i++) {
+ for (size_t i = 0; i < arr_size; i++) {
const char* field_name = build_version_constants[i].first;
const char* sysprop_name = build_version_constants[i].second;
ReloadBuildJavaConstant(env, build_version_cls, field_name, "Ljava/lang/String;", sysprop_name);
@@ -2901,7 +2901,7 @@
return -1;
}
ScopedByteArrayRO source(env, in);
- if (source.size() < length) {
+ if (source.size() < static_cast<size_t>(length)) {
// Invalid parameter
jniThrowException(env, "java/lang/IllegalArgumentException", nullptr);
return -1;
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 2b5b8f7..87ab496 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -76,7 +76,7 @@
return {};
}
fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno)));
- } else if (nread == MAX_COMMAND_BYTES - mEnd) {
+ } else if (nread == static_cast<ssize_t>(MAX_COMMAND_BYTES - mEnd)) {
// This is pessimistic by one character, but close enough.
fail_fn("ZygoteCommandBuffer overflowed: command too long");
}
@@ -136,7 +136,7 @@
}
char* countString = line.value().first; // Newline terminated.
long nArgs = atol(countString);
- if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) {
+ if (nArgs <= 0 || nArgs >= static_cast<long>(MAX_COMMAND_BYTES / 2)) {
fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs));
}
mLinesLeft = nArgs;
@@ -153,7 +153,7 @@
// As a side effect, this sets mNiceName to a non-empty string, if possible.
template<class FailFn>
bool isSimpleForkCommand(int minUid, FailFn fail_fn) {
- if (mLinesLeft <= 0 || mLinesLeft >= MAX_COMMAND_BYTES / 2) {
+ if (mLinesLeft <= 0 || mLinesLeft >= static_cast<int32_t>(MAX_COMMAND_BYTES / 2)) {
return false;
}
static const char* RUNTIME_ARGS = "--runtime-args";
@@ -179,14 +179,14 @@
if (!read_result.has_value()) {
return false;
}
- auto [arg_start, arg_end] = read_result.value();
- if (arg_end - arg_start == RA_LENGTH
- && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
+ const auto [arg_start, arg_end] = read_result.value();
+ if (static_cast<size_t>(arg_end - arg_start) == RA_LENGTH &&
+ strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
saw_runtime_args = true;
continue;
}
- if (arg_end - arg_start >= NN_LENGTH
- && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= NN_LENGTH &&
+ strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
size_t name_len = arg_end - (arg_start + NN_LENGTH);
size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1);
memcpy(mNiceName, arg_start + NN_LENGTH, copy_len);
@@ -196,21 +196,21 @@
}
continue;
}
- if (arg_end - arg_start == IW_LENGTH
- && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) == IW_LENGTH &&
+ strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
// This also removes the need for invoke-with security checks here.
return false;
}
- if (arg_end - arg_start == CZ_LENGTH
- && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) == CZ_LENGTH &&
+ strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
return false;
}
- if (arg_end - arg_start >= CA_LENGTH
- && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= CA_LENGTH &&
+ strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
return false;
}
- if (arg_end - arg_start >= SU_LENGTH
- && strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= SU_LENGTH &&
+ strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
int uid = digitsVal(arg_start + SU_LENGTH, arg_end);
if (uid < minUid) {
return false;
@@ -218,8 +218,8 @@
saw_setuid = true;
continue;
}
- if (arg_end - arg_start >= SG_LENGTH
- && strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
+ if (static_cast<size_t>(arg_end - arg_start) >= SG_LENGTH &&
+ strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
int gid = digitsVal(arg_start + SG_LENGTH, arg_end);
if (gid == -1) {
return false;
@@ -422,7 +422,7 @@
bool first_time = true;
do {
- if (credentials.uid != expected_uid) {
+ if (credentials.uid != static_cast<uid_t>(expected_uid)) {
return JNI_FALSE;
}
n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 7744786..c5ffbb7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -187,11 +187,12 @@
dumpResourceCacheUsage();
}
- return {true, IRenderPipeline::DrawResult::kUnknownTime};
+ return {true, IRenderPipeline::DrawResult::kUnknownTime, android::base::unique_fd{}};
}
-bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) {
+bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) {
GL_CHECKPOINT(LOW);
// Even if we decided to cancel the frame, from the perspective of jank
@@ -202,7 +203,7 @@
return false;
}
- *requireSwap = drew || mEglManager.damageRequiresSwap();
+ *requireSwap = drawResult.success || mEglManager.damageRequiresSwap();
if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) {
return false;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index f0461be..098a746 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -44,8 +44,9 @@
const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
const renderthread::HardwareBufferRenderParams& bufferParams) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; }
- bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
[[nodiscard]] android::base::unique_fd flush() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 86096d5..12cb69d 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -87,7 +87,7 @@
}
if (backBuffer.get() == nullptr) {
- return {false, -1};
+ return {false, -1, android::base::unique_fd{}};
}
// update the coordinates of the global light position based on surface rotation
@@ -110,10 +110,10 @@
profiler->draw(profileRenderer);
}
- nsecs_t submissionTime = IRenderPipeline::DrawResult::kUnknownTime;
+ VulkanManager::VkDrawResult drawResult;
{
ATRACE_NAME("flush commands");
- submissionTime = vulkanManager().finishFrame(backBuffer.get());
+ drawResult = vulkanManager().finishFrame(backBuffer.get());
}
layerUpdateQueue->clear();
@@ -122,11 +122,12 @@
dumpResourceCacheUsage();
}
- return {true, submissionTime};
+ return {true, drawResult.submissionTime, std::move(drawResult.presentFence)};
}
-bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) {
+bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) {
// Even if we decided to cancel the frame, from the perspective of jank
// metrics the frame was swapped at this point
currentFrameInfo->markSwapBuffers();
@@ -135,10 +136,10 @@
return false;
}
- *requireSwap = drew;
+ *requireSwap = drawResult.success;
if (*requireSwap) {
- vulkanManager().swapBuffers(mVkSurface, screenDirty);
+ vulkanManager().swapBuffers(mVkSurface, screenDirty, std::move(drawResult.presentFence));
}
return *requireSwap;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 284cde5..e2ea57d 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -44,8 +44,9 @@
const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler,
const renderthread::HardwareBufferRenderParams& bufferParams) override;
GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; }
- bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) override;
+ bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
[[nodiscard]] android::base::unique_fd flush() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9e3bb79..56b52dc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -664,8 +664,8 @@
bool didDraw = false;
int error = OK;
- bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult.success, windowDirty,
- mCurrentFrameInfo, &requireSwap);
+ bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty, mCurrentFrameInfo,
+ &requireSwap);
mCurrentFrameInfo->set(FrameInfoIndex::CommandSubmissionCompleted) = std::max(
drawResult.commandSubmissionTime, mCurrentFrameInfo->get(FrameInfoIndex::SwapBuffers));
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 6c2cb9d..9c879d5 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -61,6 +61,7 @@
// submission occurred. -1 if this time is unknown.
static constexpr nsecs_t kUnknownTime = -1;
nsecs_t commandSubmissionTime = kUnknownTime;
+ android::base::unique_fd presentFence;
};
virtual DrawResult draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
@@ -68,8 +69,9 @@
const std::vector<sp<RenderNode>>& renderNodes,
FrameInfoVisualizer* profiler,
const HardwareBufferRenderParams& bufferParams) = 0;
- virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
- FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
+ virtual bool swapBuffers(const Frame& frame, IRenderPipeline::DrawResult&,
+ const SkRect& screenDirty, FrameInfo* currentFrameInfo,
+ bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
[[nodiscard]] virtual android::base::unique_fd flush() = 0;
virtual void setHardwareBuffer(AHardwareBuffer* hardwareBuffer) = 0;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 22c5862..e706eb0 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -531,32 +531,21 @@
PFN_vkDestroySemaphore mDestroyFunction;
VkDevice mDevice;
VkSemaphore mSemaphore;
- // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia
- // (including by the GPU) and inside the VulkanManager. So we always start with two refs, one
- // owned by Skia and one owned by the VulkanManager. The refs are decremented each time
- // destroy_semaphore is called with this object. Skia will call destroy_semaphore once it is
- // done with the semaphore and the GPU has finished work on the semaphore. The VulkanManager
- // calls destroy_semaphore after sending the semaphore to Skia and exporting it if need be.
- int mRefs = 2;
DestroySemaphoreInfo(PFN_vkDestroySemaphore destroyFunction, VkDevice device,
VkSemaphore semaphore)
: mDestroyFunction(destroyFunction), mDevice(device), mSemaphore(semaphore) {}
+
+ ~DestroySemaphoreInfo() { mDestroyFunction(mDevice, mSemaphore, nullptr); }
};
static void destroy_semaphore(void* context) {
DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(context);
- --info->mRefs;
- if (!info->mRefs) {
- info->mDestroyFunction(info->mDevice, info->mSemaphore, nullptr);
- delete info;
- }
+ delete info;
}
-nsecs_t VulkanManager::finishFrame(SkSurface* surface) {
+VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ATRACE_NAME("Vulkan finish frame");
- ALOGE_IF(mSwapSemaphore != VK_NULL_HANDLE || mDestroySemaphoreContext != nullptr,
- "finishFrame already has an outstanding semaphore");
VkExportSemaphoreCreateInfo exportInfo;
exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
@@ -576,11 +565,11 @@
GrFlushInfo flushInfo;
if (err == VK_SUCCESS) {
- mDestroySemaphoreContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
flushInfo.fNumSemaphores = 1;
flushInfo.fSignalSemaphores = &backendSemaphore;
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = mDestroySemaphoreContext;
+ flushInfo.fFinishedContext =
+ new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
} else {
semaphore = VK_NULL_HANDLE;
}
@@ -589,10 +578,11 @@
GrSemaphoresSubmitted submitted = context->flush(
surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
context->submit();
- const nsecs_t submissionTime = systemTime();
+ VkDrawResult drawResult{
+ .submissionTime = systemTime(),
+ };
if (semaphore != VK_NULL_HANDLE) {
if (submitted == GrSemaphoresSubmitted::kYes) {
- mSwapSemaphore = semaphore;
if (mFrameBoundaryANDROID) {
// retrieve VkImage used as render target
VkImage image = VK_NULL_HANDLE;
@@ -611,45 +601,37 @@
}
// frameBoundaryANDROID needs to know about mSwapSemaphore, but
// it won't wait on it.
- mFrameBoundaryANDROID(mDevice, mSwapSemaphore, image);
+ mFrameBoundaryANDROID(mDevice, semaphore, image);
}
- } else {
- destroy_semaphore(mDestroySemaphoreContext);
- mDestroySemaphoreContext = nullptr;
}
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ int fenceFd = -1;
+ err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
+ drawResult.presentFence.reset(fenceFd);
+ } else {
+ ALOGE("VulkanManager::finishFrame(): Semaphore submission failed");
+ mQueueWaitIdle(mGraphicsQueue);
}
+
skiapipeline::ShaderCache::get().onVkFrameFlushed(context);
- return submissionTime;
+ return drawResult;
}
-void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) {
+void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect,
+ android::base::unique_fd&& presentFence) {
if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
mDeviceWaitIdle(mDevice);
}
- int fenceFd = -1;
- if (mSwapSemaphore != VK_NULL_HANDLE) {
- VkSemaphoreGetFdInfoKHR getFdInfo;
- getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
- getFdInfo.pNext = nullptr;
- getFdInfo.semaphore = mSwapSemaphore;
- getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
- ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
- } else {
- ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed");
- mQueueWaitIdle(mGraphicsQueue);
- }
- if (mDestroySemaphoreContext) {
- destroy_semaphore(mDestroySemaphoreContext);
- }
-
- surface->presentCurrentBuffer(dirtyRect, fenceFd);
- mSwapSemaphore = VK_NULL_HANDLE;
- mDestroySemaphoreContext = nullptr;
+ surface->presentCurrentBuffer(dirtyRect, presentFence.release());
}
void VulkanManager::destroySurface(VulkanSurface* surface) {
@@ -753,22 +735,17 @@
GrBackendSemaphore backendSemaphore;
backendSemaphore.initVulkan(semaphore);
- DestroySemaphoreInfo* destroyInfo =
- new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
// Even if Skia fails to submit the semaphore, it will still call the destroy_semaphore callback
- // which will remove its ref to the semaphore. The VulkanManager must still release its ref,
- // when it is done with the semaphore.
GrFlushInfo flushInfo;
flushInfo.fNumSemaphores = 1;
flushInfo.fSignalSemaphores = &backendSemaphore;
flushInfo.fFinishedProc = destroy_semaphore;
- flushInfo.fFinishedContext = destroyInfo;
+ flushInfo.fFinishedContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore);
GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
grContext->submit();
if (submitted == GrSemaphoresSubmitted::kNo) {
ALOGE("VulkanManager::createReleaseFence: Failed to submit semaphore");
- destroy_semaphore(destroyInfo);
return INVALID_OPERATION;
}
@@ -781,7 +758,6 @@
int fenceFd = 0;
err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
- destroy_semaphore(destroyInfo);
if (VK_SUCCESS != err) {
ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd");
return INVALID_OPERATION;
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index dbef7fb..b92ebb3 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -22,6 +22,7 @@
#endif
#include <GrContextOptions.h>
#include <SkSurface.h>
+#include <android-base/unique_fd.h>
#include <utils/StrongPointer.h>
#include <vk/GrVkBackendContext.h>
#include <vk/GrVkExtensions.h>
@@ -82,10 +83,17 @@
void destroySurface(VulkanSurface* surface);
Frame dequeueNextBuffer(VulkanSurface* surface);
+
+ struct VkDrawResult {
+ // The estimated start time for intiating GPU work, -1 if unknown.
+ nsecs_t submissionTime;
+ android::base::unique_fd presentFence;
+ };
+
// Finishes the frame and submits work to the GPU
- // Returns the estimated start time for intiating GPU work, -1 otherwise.
- nsecs_t finishFrame(SkSurface* surface);
- void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect);
+ VkDrawResult finishFrame(SkSurface* surface);
+ void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect,
+ android::base::unique_fd&& presentFence);
// Inserts a wait on fence command into the Vulkan command buffer.
status_t fenceWait(int fence, GrDirectContext* grContext);
@@ -201,9 +209,6 @@
GrVkExtensions mExtensions;
uint32_t mDriverVersion = 0;
- VkSemaphore mSwapSemaphore = VK_NULL_HANDLE;
- void* mDestroySemaphoreContext = nullptr;
-
std::once_flag mInitFlag;
std::atomic_bool mInitialized = false;
};
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
index 1a9ebbc..d607126 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroup.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java
@@ -17,6 +17,7 @@
package android.media.audiopolicy;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
@@ -107,7 +108,7 @@
}
@Override
- public boolean equals(@NonNull Object o) {
+ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index 08a8d89..2769dbc 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -127,7 +127,7 @@
if (item->getTimestamp() > 0) {
bh.put(mediametrics::BUNDLE_TIMESTAMP, (int64_t)item->getTimestamp());
}
- if (item->getUid() != -1) {
+ if (static_cast<int32_t>(item->getUid()) != -1) {
bh.put(mediametrics::BUNDLE_UID, (int32_t)item->getUid());
}
for (const auto &prop : *item) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f074106..f03263b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -23,6 +23,7 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
+import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
@@ -704,12 +705,23 @@
continue;
}
for (int complianceWarningType : complianceWarnings) {
- switch (complianceWarningType) {
- case UsbPortStatus.COMPLIANCE_WARNING_OTHER:
- case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
- return true;
- default:
- break;
+ if (Flags.enableUsbDataComplianceWarning()
+ && Flags.enableInputPowerLimitedWarning()) {
+ switch (complianceWarningType) {
+ case UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED:
+ case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
+ return true;
+ default:
+ break;
+ }
+ } else {
+ switch (complianceWarningType) {
+ case UsbPortStatus.COMPLIANCE_WARNING_OTHER:
+ case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY:
+ return true;
+ default:
+ break;
+ }
}
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 29846ac..a88a9c7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.hardware.usb.flags.Flags;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
@@ -36,6 +37,7 @@
import android.os.BatteryManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.telephony.AccessNetworkConstants;
import android.telephony.NetworkRegistrationInfo;
@@ -44,6 +46,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@@ -85,6 +88,8 @@
@Mock
private UsbPortStatus mUsbPortStatus;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -425,8 +430,38 @@
}
@Test
- public void containsIncompatibleChargers_complianeWarningOther_returnTrue() {
+ public void containsIncompatibleChargers_complianeWarningOther_returnTrue_flagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningPower_returnFalse_flagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningOther_returnFalse_flagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+
+ assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+ }
+
+ @Test
+ public void containsIncompatibleChargers_complianeWarningPower_returnTrue_flagEnabled() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_COMPLIANCE_WARNING);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_INPUT_POWER_LIMITED_WARNING);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_INPUT_POWER_LIMITED);
+
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
}
@@ -446,7 +481,6 @@
public void containsIncompatibleChargers_emptyComplianceWarnings_returnFalse() {
setupIncompatibleCharging();
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
-
assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
}
@@ -476,7 +510,7 @@
}
private void setupIncompatibleCharging() {
- setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER);
+ setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY);
}
private void setupIncompatibleCharging(int complianceWarningType) {
diff --git a/packages/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 77b844d..1429782 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -25,6 +25,7 @@
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 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 @@
CommunalContent(
modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth),
model = communalContent[index],
+ viewModel = viewModel,
deleteOnClick = viewModel::onDeleteWidget,
size =
SizeF(
@@ -94,6 +98,7 @@
@Composable
private fun CommunalContent(
model: CommunalContentModel,
+ viewModel: CommunalViewModel,
size: SizeF,
deleteOnClick: (id: Int) -> Unit,
modifier: Modifier = Modifier,
@@ -102,6 +107,7 @@
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 @@
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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9a3c6d5..8780f58 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/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 6b8009d..32f9c30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -605,7 +605,6 @@
mAodIconsViewModel,
mConfigurationState,
mConfigurationController,
- mDozeParameters,
mAodIconViewStore);
final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
nic,
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 c3421de..273adcf 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 @@
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 0000000..e41c322
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
+
+/** Encapsulates the state of smartspace in communal. */
+interface CommunalMediaRepository {
+ val mediaPlaying: Flow<Boolean>
+}
+
+@SysUISingleton
+class CommunalMediaRepositoryImpl
+@Inject
+constructor(
+ private val mediaDataManager: MediaDataManager,
+) : CommunalMediaRepository {
+
+ private val mediaDataListener =
+ object : MediaDataManager.Listener {
+ override fun onMediaDataLoaded(
+ key: String,
+ oldKey: String?,
+ data: MediaData,
+ immediately: Boolean,
+ receivedSmartspaceCardLatency: Int,
+ isSsReactivated: Boolean
+ ) {
+ if (!mediaDataManager.hasAnyMediaOrRecommendation()) {
+ return
+ }
+ _mediaPlaying.value = true
+ }
+
+ override fun onMediaDataRemoved(key: String) {
+ if (mediaDataManager.hasAnyMediaOrRecommendation()) {
+ return
+ }
+ _mediaPlaying.value = false
+ }
+ }
+
+ private val _mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+ override val mediaPlaying: Flow<Boolean> =
+ _mediaPlaying
+ .onStart {
+ mediaDataManager.addListener(mediaDataListener)
+ _mediaPlaying.value = mediaDataManager.hasAnyMediaOrRecommendation()
+ }
+ .onCompletion { mediaDataManager.removeListener(mediaDataListener) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt
new file mode 100644
index 0000000..2c6d9e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface CommunalMediaRepositoryModule {
+ @Binds fun communalMediaRepository(impl: CommunalMediaRepositoryImpl): CommunalMediaRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 524cccf..eb36b19 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -19,6 +19,7 @@
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.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 @@
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 @@
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 69382a5..bb9b4b5 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 @@
) : 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/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index d8d1dc0..702554a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -33,8 +33,8 @@
defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
- override val sections: Set<KeyguardSection> =
- setOf(
+ override val sections: List<KeyguardSection> =
+ listOf(
defaultCommunalHubSection,
defaultCommunalWidgetSection,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 3df6e7e..5efe6ce 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -21,7 +21,10 @@
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 @@
@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 b98794e..9bb23d8 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/SmartSpaceComplication.java
@@ -71,6 +71,18 @@
private final DreamOverlayStateController mDreamOverlayStateController;
private final SmartSpaceComplication mComplication;
private final FeatureFlags mFeatureFlags;
+ private final DreamOverlayStateController.Callback mStateControllerCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ if (mDreamOverlayStateController.isOverlayActive()) {
+ mSmartSpaceController.addListener(mSmartspaceListener);
+ } else {
+ mSmartSpaceController.removeListener(mSmartspaceListener);
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+ }
+ };
private final BcSmartspaceDataPlugin.SmartspaceTargetListener mSmartspaceListener =
new BcSmartspaceDataPlugin.SmartspaceTargetListener() {
@@ -103,17 +115,7 @@
return;
}
- mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
- @Override
- public void onStateChanged() {
- if (mDreamOverlayStateController.isOverlayActive()) {
- mSmartSpaceController.addListener(mSmartspaceListener);
- } else {
- mSmartSpaceController.removeListener(mSmartspaceListener);
- mDreamOverlayStateController.removeComplication(mComplication);
- }
- }
- });
+ mDreamOverlayStateController.addCallback(mStateControllerCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 8eb80ad..b34b459 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -130,6 +130,7 @@
import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
import com.android.systemui.util.dagger.UtilModule;
import com.android.systemui.util.kotlin.CoroutinesModule;
+import com.android.systemui.util.reference.ReferenceModule;
import com.android.systemui.util.sensors.SensorModule;
import com.android.systemui.util.settings.SettingsUtilModule;
import com.android.systemui.util.time.SystemClock;
@@ -201,6 +202,7 @@
PrivacyModule.class,
QRCodeScannerModule.class,
QSFragmentStartableModule.class,
+ ReferenceModule.class,
RetailModeModule.class,
ScreenshotModule.class,
SensorModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index c9748f9..0e333f2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -31,11 +31,15 @@
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.DreamLog;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.util.annotations.WeaklyReferencedCallback;
+import com.android.systemui.util.reference.WeakReferenceFactory;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -68,7 +72,10 @@
/**
* Callback for dream overlay events.
+ * NOTE: Caller should maintain a strong reference to this themselves so the callback does
+ * not get garbage collected.
*/
+ @WeaklyReferencedCallback
public interface Callback {
/**
* Called when the composition of complications changes.
@@ -97,7 +104,7 @@
private final Executor mExecutor;
private final boolean mOverlayEnabled;
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
@Complication.ComplicationType
private int mAvailableComplicationTypes = Complication.COMPLICATION_TYPE_NONE;
@@ -107,6 +114,7 @@
private final Collection<Complication> mComplications = new HashSet();
private final FeatureFlags mFeatureFlags;
+ private final WeakReferenceFactory mWeakReferenceFactory;
private final int mSupportedTypes;
@@ -117,11 +125,13 @@
public DreamOverlayStateController(@Main Executor executor,
@Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled,
FeatureFlags featureFlags,
- @DreamLog LogBuffer logBuffer) {
+ @DreamLog LogBuffer logBuffer,
+ WeakReferenceFactory weakReferenceFactory) {
mExecutor = executor;
mOverlayEnabled = overlayEnabled;
mLogger = new DreamLogger(logBuffer, TAG);
mFeatureFlags = featureFlags;
+ mWeakReferenceFactory = weakReferenceFactory;
if (mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)) {
mSupportedTypes = Complication.COMPLICATION_TYPE_NONE
| Complication.COMPLICATION_TYPE_HOME_CONTROLS;
@@ -143,7 +153,7 @@
mExecutor.execute(() -> {
if (mComplications.add(complication)) {
mLogger.logAddComplication(complication.toString());
- mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
+ notifyCallbacksLocked(Callback::onComplicationsChanged);
}
});
}
@@ -160,7 +170,7 @@
mExecutor.execute(() -> {
if (mComplications.remove(complication)) {
mLogger.logRemoveComplication(complication.toString());
- mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
+ notifyCallbacksLocked(Callback::onComplicationsChanged);
}
});
}
@@ -199,22 +209,33 @@
}
private void notifyCallbacks(Consumer<Callback> callbackConsumer) {
- mExecutor.execute(() -> {
- for (Callback callback : mCallbacks) {
+ mExecutor.execute(() -> notifyCallbacksLocked(callbackConsumer));
+ }
+
+ private void notifyCallbacksLocked(Consumer<Callback> callbackConsumer) {
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback callback = iterator.next().get();
+ // Remove any callbacks which have been GC'd
+ if (callback == null) {
+ iterator.remove();
+ } else {
callbackConsumer.accept(callback);
}
- });
+ }
}
@Override
public void addCallback(@NonNull Callback callback) {
mExecutor.execute(() -> {
Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- if (mCallbacks.contains(callback)) {
+ final boolean containsCallback = mCallbacks.stream()
+ .anyMatch(reference -> reference.get() == callback);
+ if (containsCallback) {
return;
}
- mCallbacks.add(callback);
+ mCallbacks.add(mWeakReferenceFactory.create(callback));
if (mComplications.isEmpty()) {
return;
@@ -228,7 +249,13 @@
public void removeCallback(@NonNull Callback callback) {
mExecutor.execute(() -> {
Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
- mCallbacks.remove(callback);
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
});
}
@@ -318,7 +345,7 @@
if (isLowLightActive() && !active) {
// Notify that we're exiting low light only on the transition from active to not active.
- mCallbacks.forEach(Callback::onExitLowLight);
+ notifyCallbacks(Callback::onExitLowLight);
}
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE);
}
@@ -375,7 +402,7 @@
mExecutor.execute(() -> {
mLogger.logAvailableComplicationTypes(types);
mAvailableComplicationTypes = types;
- mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
+ notifyCallbacksLocked(Callback::onAvailableComplicationTypesChanged);
});
}
@@ -393,7 +420,7 @@
mExecutor.execute(() -> {
mLogger.logShouldShowComplications(shouldShowComplications);
mShouldShowComplications = shouldShowComplications;
- mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
+ notifyCallbacksLocked(Callback::onAvailableComplicationTypesChanged);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/view/KeyboardBacklightDialog.kt
index 1e9be09..e16bb0b 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.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 @@
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 @@
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 @@
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 654f2d1..f5f5571 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -66,7 +66,7 @@
/**
* Acts as source of truth for biometric authentication related settings like enrollments, device
- * policy, etc.
+ * policy specifically for device entry usage.
*
* Abstracts-away data sources and their schemas so the rest of the app doesn't need to worry about
* upstream changes.
@@ -74,7 +74,8 @@
interface BiometricSettingsRepository {
/**
* If the current user can enter the device using fingerprint. This is true if user has enrolled
- * fingerprints and fingerprint auth is not disabled through settings/device policy
+ * fingerprints and fingerprint auth is not disabled for device entry through settings and
+ * device policy
*/
val isFingerprintEnrolledAndEnabled: StateFlow<Boolean>
@@ -247,9 +248,11 @@
}
}
- private val isFaceEnabledByBiometricsManagerForCurrentUser: Flow<Boolean> =
+ private val areBiometricsEnabledForCurrentUser: Flow<Boolean> =
userRepository.selectedUserInfo.flatMapLatest { userInfo ->
- isFaceEnabledByBiometricsManager.map { biometricsEnabledForUser[userInfo.id] ?: false }
+ areBiometricsEnabledForDeviceEntryFromUserSetting.map {
+ biometricsEnabledForUser[userInfo.id] ?: false
+ }
}
private val isFaceEnabledByDevicePolicy: Flow<Boolean> =
@@ -263,13 +266,13 @@
.distinctUntilChanged()
private val isFaceAuthenticationEnabled: Flow<Boolean> =
- combine(isFaceEnabledByBiometricsManagerForCurrentUser, isFaceEnabledByDevicePolicy) {
+ combine(areBiometricsEnabledForCurrentUser, isFaceEnabledByDevicePolicy) {
biometricsManagerSetting,
devicePolicySetting ->
biometricsManagerSetting && devicePolicySetting
}
- private val isFaceEnabledByBiometricsManager: Flow<Pair<Int, Boolean>> =
+ private val areBiometricsEnabledForDeviceEntryFromUserSetting: Flow<Pair<Int, Boolean>> =
conflatedCallbackFlow {
val callback =
object : IBiometricEnabledOnKeyguardCallback.Stub() {
@@ -340,6 +343,7 @@
override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> =
isFingerprintEnrolled
+ .and(areBiometricsEnabledForCurrentUser)
.and(isFingerprintEnabledByDevicePolicy)
.stateIn(scope, SharingStarted.Eagerly, false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index f5ef27d..fbd62ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -17,33 +17,29 @@
package com.android.systemui.keyguard.data.repository
+import android.util.Log
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
import java.io.PrintWriter
import java.util.TreeMap
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-import kotlinx.coroutines.launch
/**
* Manages blueprint changes for the lockscreen.
*
* To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in
- * the dagger module:
+ * the dagger module: [KeyguardBlueprintModule]
*
* A Blueprint determines how the layout should be constrained on a high level.
*
* A Section is a modular piece of code that implements the constraints. The blueprint uses the
* sections to define the constraints.
- *
- * @see KeyguardBlueprintModule
*/
@SysUISingleton
class KeyguardBlueprintRepository
@@ -51,18 +47,16 @@
constructor(
configurationRepository: ConfigurationRepository,
blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
- @Application private val applicationScope: CoroutineScope,
) {
private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> = TreeMap()
private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1)
val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow()
+ val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange
+
init {
blueprintIdMap.putAll(blueprints.associateBy { it.id })
applyBlueprint(blueprintIdMap[DEFAULT]!!)
- applicationScope.launch {
- configurationRepository.onAnyConfigurationChange.collect { refreshBlueprint() }
- }
}
/**
@@ -86,9 +80,18 @@
* @return whether the transition has succeeded.
*/
fun applyBlueprint(blueprintId: String?): Boolean {
- val blueprint = blueprintIdMap[blueprintId] ?: return false
- applyBlueprint(blueprint)
- return true
+ val blueprint = blueprintIdMap[blueprintId]
+ return if (blueprint != null) {
+ applyBlueprint(blueprint)
+ true
+ } else {
+ Log.e(
+ TAG,
+ "Could not find blueprint with id: $blueprintId. " +
+ "Perhaps it was not added to KeyguardBlueprintModule?"
+ )
+ false
+ }
}
/** Emits the blueprint value to the collectors. */
@@ -107,4 +110,8 @@
fun printBlueprints(pw: PrintWriter) {
blueprintIdMap.onEachIndexed { index, entry -> pw.println("$index: ${entry.key}") }
}
+
+ companion object {
+ private const val TAG = "KeyguardBlueprintRepository"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 6ce9185..7dab84d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -17,16 +17,56 @@
package com.android.systemui.keyguard.domain.interactor
+import android.content.Context
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
+import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
@SysUISingleton
class KeyguardBlueprintInteractor
@Inject
-constructor(private val keyguardBlueprintRepository: KeyguardBlueprintRepository) {
+constructor(
+ private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
+ @Application private val applicationScope: CoroutineScope,
+ private val context: Context,
+ private val splitShadeStateController: SplitShadeStateController,
+) {
+
val blueprint = keyguardBlueprintRepository.blueprint
+ init {
+ applicationScope.launch {
+ keyguardBlueprintRepository.configurationChange
+ .onStart { emit(Unit) }
+ .collect { updateBlueprint() }
+ }
+ }
+
+ /**
+ * Detects when a new blueprint should be applied and calls [transitionToBlueprint]. This may
+ * end up reapplying the same blueprint, which is fine as configuration may have changed.
+ */
+ private fun updateBlueprint() {
+ val useSplitShade =
+ splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
+
+ val blueprintId =
+ if (useSplitShade) {
+ SplitShadeKeyguardBlueprint.ID
+ } else {
+ DefaultKeyguardBlueprint.DEFAULT
+ }
+
+ transitionToBlueprint(blueprintId)
+ }
+
/**
* Transitions to a blueprint.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 7fc1911..3440440 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -22,7 +22,7 @@
/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
interface KeyguardBlueprint {
val id: String
- val sections: Set<KeyguardSection>
+ val sections: List<KeyguardSection>
/**
* Removes views of old blueprint and add views of new blueprint.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index f1ea446..2e64c41 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -32,7 +32,6 @@
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
-import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
/**
@@ -53,7 +52,6 @@
defaultStatusViewSection: DefaultStatusViewSection,
defaultStatusBarSection: DefaultStatusBarSection,
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
- splitShadeGuidelines: SplitShadeGuidelines,
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
@@ -63,7 +61,7 @@
override val id: String = DEFAULT
override val sections =
- setOf(
+ listOf(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
defaultShortcutsSection,
@@ -72,7 +70,6 @@
defaultStatusViewSection,
defaultStatusBarSection,
defaultNotificationStackScrollLayoutSection,
- splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
communalTutorialIndicatorSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index fda4c3d..4f1a754 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -33,6 +33,12 @@
@Binds
@IntoSet
+ abstract fun bindSplitShadeBlueprint(
+ splitShadeBlueprint: SplitShadeKeyguardBlueprint
+ ): KeyguardBlueprint
+
+ @Binds
+ @IntoSet
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index f8dd7c1..d8b368b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -52,7 +52,7 @@
override val id: String = SHORTCUTS_BESIDE_UDFPS
override val sections =
- setOf(
+ listOf(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
defaultAmbientIndicationAreaSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
new file mode 100644
index 0000000..35679b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.blueprints
+
+import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import javax.inject.Inject
+
+/**
+ * Split-shade layout, mostly used for larger devices like foldables and tablets when in landscape
+ * orientation.
+ */
+@SysUISingleton
+@JvmSuppressWildcards
+class SplitShadeKeyguardBlueprint
+@Inject
+constructor(
+ defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
+ defaultShortcutsSection: DefaultShortcutsSection,
+ defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ defaultStatusViewSection: DefaultStatusViewSection,
+ defaultStatusBarSection: DefaultStatusBarSection,
+ splitShadeNotificationStackScrollLayoutSection: SplitShadeNotificationStackScrollLayoutSection,
+ splitShadeGuidelines: SplitShadeGuidelines,
+ aodNotificationIconsSection: AodNotificationIconsSection,
+ aodBurnInSection: AodBurnInSection,
+ communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
+) : KeyguardBlueprint {
+ override val id: String = ID
+
+ override val sections =
+ listOf(
+ defaultIndicationAreaSection,
+ defaultDeviceEntryIconSection,
+ defaultShortcutsSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ defaultStatusViewSection,
+ defaultStatusBarSection,
+ splitShadeNotificationStackScrollLayoutSection,
+ splitShadeGuidelines,
+ aodNotificationIconsSection,
+ aodBurnInSection,
+ communalTutorialIndicatorSection,
+ )
+
+ companion object {
+ const val ID = "split-shade"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 06bb0a6..975d62a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -94,7 +94,6 @@
nicAodViewModel,
configurationState,
configurationController,
- dozeParameters,
nicAodIconViewStore,
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 1995661..078feff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -18,9 +18,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.View
-import android.view.ViewGroup
-import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -29,55 +26,34 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
+/** Single column format for notifications (default for phones) */
class DefaultNotificationStackScrollLayoutSection
@Inject
constructor(
- private val context: Context,
- private val featureFlags: FeatureFlags,
- private val notificationPanelView: NotificationPanelView,
- private val sharedNotificationContainer: SharedNotificationContainer,
- private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
- private val controller: NotificationStackScrollLayoutController,
- private val smartspaceViewModel: KeyguardSmartspaceViewModel
-) : KeyguardSection() {
- private val placeHolderId = R.id.nssl_placeholder
-
- override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- return
- }
- // This moves the existing NSSL view to a different parent, as the controller is a
- // singleton and recreating it has other bad side effects
- notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
- (it.parent as ViewGroup).removeView(it)
- sharedNotificationContainer.addNotificationStackScrollLayout(it)
- }
-
- val view = View(context, null).apply { id = placeHolderId }
- constraintLayout.addView(view)
- }
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- return
- }
- SharedNotificationContainerBinder.bind(
- sharedNotificationContainer,
- sharedNotificationContainerViewModel,
- controller,
- )
- }
-
+ context: Context,
+ featureFlags: FeatureFlags,
+ notificationPanelView: NotificationPanelView,
+ sharedNotificationContainer: SharedNotificationContainer,
+ sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ controller: NotificationStackScrollLayoutController,
+ private val smartspaceViewModel: KeyguardSmartspaceViewModel,
+) :
+ NotificationStackScrollLayoutSection(
+ context,
+ featureFlags,
+ notificationPanelView,
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ ) {
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
return
@@ -85,41 +61,29 @@
constraintSet.apply {
val bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
- val useSplitShade =
- context.resources.getBoolean(R.bool.config_use_split_notification_shade)
- val topAlignment =
- if (useSplitShade) {
- TOP
- } else {
- BOTTOM
- }
if (featureFlags.isEnabled(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT)) {
connect(
R.id.nssl_placeholder,
TOP,
smartspaceViewModel.smartspaceViewId,
- topAlignment,
+ BOTTOM,
bottomMargin
)
setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin)
} else {
- connect(
- R.id.nssl_placeholder,
- TOP,
- R.id.keyguard_status_view,
- topAlignment,
- bottomMargin
- )
+ connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, BOTTOM, bottomMargin)
}
-
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
- connect(R.id.nssl_placeholder, BOTTOM, R.id.lock_icon_view, TOP)
- }
- }
- override fun removeViews(constraintLayout: ConstraintLayout) {
- constraintLayout.removeView(placeHolderId)
+ val lockId =
+ if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+ connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
new file mode 100644
index 0000000..00966f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import kotlinx.coroutines.DisposableHandle
+
+abstract class NotificationStackScrollLayoutSection
+constructor(
+ protected val context: Context,
+ protected val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val sharedNotificationContainer: SharedNotificationContainer,
+ private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val controller: NotificationStackScrollLayoutController,
+) : KeyguardSection() {
+ private val placeHolderId = R.id.nssl_placeholder
+ private var disposableHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ // This moves the existing NSSL view to a different parent, as the controller is a
+ // singleton and recreating it has other bad side effects
+ notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ sharedNotificationContainer.addNotificationStackScrollLayout(it)
+ }
+
+ val view = View(context, null).apply { id = placeHolderId }
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ disposableHandle?.dispose()
+ disposableHandle =
+ SharedNotificationContainerBinder.bind(
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ )
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ disposableHandle?.dispose()
+ constraintLayout.removeView(placeHolderId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
new file mode 100644
index 0000000..bf95c77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import javax.inject.Inject
+
+/** Large-screen format for notifications, shown as two columns on the device */
+class SplitShadeNotificationStackScrollLayoutSection
+@Inject
+constructor(
+ context: Context,
+ featureFlags: FeatureFlags,
+ notificationPanelView: NotificationPanelView,
+ sharedNotificationContainer: SharedNotificationContainer,
+ sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ controller: NotificationStackScrollLayoutController,
+ private val smartspaceViewModel: KeyguardSmartspaceViewModel,
+) :
+ NotificationStackScrollLayoutSection(
+ context,
+ featureFlags,
+ notificationPanelView,
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ ) {
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ constraintSet.apply {
+ val bottomMargin =
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
+
+ if (featureFlags.isEnabled(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT)) {
+ connect(
+ R.id.nssl_placeholder,
+ TOP,
+ smartspaceViewModel.smartspaceViewId,
+ TOP,
+ bottomMargin
+ )
+ setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin)
+ } else {
+ connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, TOP, bottomMargin)
+ }
+ connect(R.id.nssl_placeholder, START, PARENT_ID, START)
+ connect(R.id.nssl_placeholder, END, PARENT_ID, END)
+
+ val lockId =
+ if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+ connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 3bf8057..843a454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -61,6 +61,7 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
import com.android.systemui.util.drawable.DrawableSize;
import java.lang.annotation.Retention;
@@ -164,7 +165,6 @@
private int mDrawableColor;
private int mIconColor;
private int mDecorColor;
- private float mDozeAmount;
private ValueAnimator mColorAnimator;
private int mCurrentSetColor = NO_COLOR;
private int mAnimationStartColor = NO_COLOR;
@@ -174,7 +174,6 @@
animation.getAnimatedFraction());
setColorInternal(newColor);
};
- private final NotificationDozeHelper mDozer;
private int mContrastedDrawableColor;
private int mCachedContrastBackgroundColor = NO_COLOR;
private float[] mMatrix;
@@ -184,6 +183,8 @@
private Runnable mOnDismissListener;
private boolean mIncreasedSize;
private boolean mShowsConversation;
+ private float mDozeAmount;
+ private final NotificationDozeHelper mDozer;
public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
this(context, slot, sbn, false);
@@ -958,18 +959,28 @@
return mDotAppearAmount;
}
- public void setDozing(boolean dozing, boolean fade, long delay) {
- setDozing(dozing, fade, delay, /* onChildCompleted= */ null);
+ public void setDozing(boolean dozing, boolean animate, long delay) {
+ setDozing(dozing, animate, delay, /* onChildCompleted= */ null);
}
- public void setDozing(boolean dozing, boolean fade, long delay,
+ public void setTintAlpha(float tintAlpha) {
+ if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
+ setDozeAmount(tintAlpha);
+ }
+
+ private void setDozeAmount(float dozeAmount) {
+ mDozeAmount = dozeAmount;
+ updateDecorColor();
+ updateIconColor();
+ }
+
+ public void setDozing(boolean dozing, boolean animate, long delay,
@Nullable Runnable endRunnable) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
mDozer.setDozing(f -> {
- mDozeAmount = f;
- updateDecorColor();
- updateIconColor();
+ setDozeAmount(f);
updateAllowAnimation();
- }, dozing, fade, delay, this, endRunnable);
+ }, dozing, animate, delay, this, endRunnable);
}
private void updateAllowAnimation() {
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 62c3e9e..2ea7f61 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
@@ -28,15 +28,12 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
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.NotificationIconColors
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
-import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
@@ -44,7 +41,6 @@
import com.android.systemui.util.kotlin.mapValuesNotNullTo
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.stateFlow
-import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
@@ -95,7 +91,11 @@
configurationController,
viewStore,
) { _, sbiv ->
- iconColors.collect { sbiv.updateTintForIcon(it, contrastColorUtil) }
+ StatusBarIconViewBinder.bindIconColors(
+ sbiv,
+ iconColors,
+ contrastColorUtil,
+ )
}
}
launch { viewModel.bindIsolatedIcon(view, viewStore) }
@@ -110,7 +110,6 @@
viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
configuration: ConfigurationState,
configurationController: ConfigurationController,
- dozeParameters: DozeParameters,
viewStore: IconViewStore,
): DisposableHandle {
return view.repeatWhenAttached {
@@ -122,48 +121,37 @@
configurationController,
viewStore,
) { _, sbiv ->
- configuration
- .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
- .collect { tint ->
- sbiv.staticDrawableColor = tint
- sbiv.setDecorColor(tint)
- }
+ viewModel.bindAodStatusBarIconView(sbiv, configuration)
}
}
- launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
- launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
+ launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
}
}
}
+ private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView(
+ sbiv: StatusBarIconView,
+ configuration: ConfigurationState,
+ ) {
+ coroutineScope {
+ launch {
+ val color: Flow<Int> =
+ configuration.getColorAttr(
+ R.attr.wallpaperTextColor,
+ DEFAULT_AOD_ICON_COLOR,
+ )
+ StatusBarIconViewBinder.bindColor(sbiv, color)
+ }
+ launch { StatusBarIconViewBinder.bindTintAlpha(sbiv, tintAlpha) }
+ launch { StatusBarIconViewBinder.bindAnimationsEnabled(sbiv, areIconAnimationsEnabled) }
+ }
+ }
+
/** Binds to [NotificationIconContainer.setAnimationsEnabled] */
private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
collect(view::setAnimationsEnabled)
}
- private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
- view: NotificationIconContainer,
- dozeParameters: DozeParameters,
- ) {
- collect { isDozing ->
- if (isDozing.isAnimating) {
- val animate = !dozeParameters.displayNeedsBlanking
- view.setDozing(
- /* dozing = */ isDozing.value,
- /* fade = */ animate,
- /* delay = */ 0,
- /* endRunnable = */ isDozing::stopAnimating,
- )
- } else {
- view.setDozing(
- /* dozing = */ isDozing.value,
- /* fade= */ false,
- /* delay= */ 0,
- )
- }
- }
- }
-
private suspend fun NotificationIconContainerStatusBarViewModel.bindIsolatedIcon(
view: NotificationIconContainer,
viewStore: IconViewStore,
@@ -261,6 +249,7 @@
// and added again
view.removeTransientView(sbiv)
view.addView(sbiv, i, layoutParams)
+ iconBindings.remove(key)?.cancel()
iconBindings[key] = launch { bindIcon(key, sbiv) }
}
@@ -282,18 +271,6 @@
}
}
- // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
- // can be moved there and cleaned up.
- private fun StatusBarIconView.updateTintForIcon(
- iconColors: NotificationIconColors,
- contrastColorUtil: ContrastColorUtil,
- ) {
- val isPreL = java.lang.Boolean.TRUE == getTag(R.id.icon_is_pre_L)
- val isColorized = !isPreL || NotificationUtils.isGrayscale(this, contrastColorUtil)
- staticDrawableColor = iconColors.staticDrawableColor(viewBounds, isColorized)
- setDecorColor(iconColors.tint)
- }
-
/** External storage for [StatusBarIconView] instances. */
fun interface IconViewStore {
fun iconView(key: String): StatusBarIconView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
new file mode 100644
index 0000000..3a2e21a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.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.statusbar.notification.icon.ui.viewbinder
+
+import android.graphics.Rect
+import android.view.View
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
+import kotlinx.coroutines.flow.Flow
+
+object StatusBarIconViewBinder {
+
+ // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, these
+ // methods can become private and we can have a single bind() method for SBIV and its
+ // view-model (which, at the time of this writing, does not yet exist).
+
+ suspend fun bindColor(view: StatusBarIconView, color: Flow<Int>) {
+ color.collect { color ->
+ view.staticDrawableColor = color
+ view.setDecorColor(color)
+ }
+ }
+
+ suspend fun bindTintAlpha(view: StatusBarIconView, tintAlpha: Flow<Float>) {
+ tintAlpha.collect { amt -> view.setTintAlpha(amt) }
+ }
+
+ suspend fun bindAnimationsEnabled(view: StatusBarIconView, allowAnimation: Flow<Boolean>) {
+ allowAnimation.collect(view::setAllowAnimation)
+ }
+
+ suspend fun bindIconColors(
+ view: StatusBarIconView,
+ iconColors: Flow<NotificationIconColors>,
+ contrastColorUtil: ContrastColorUtil,
+ ) {
+ iconColors.collect { colors ->
+ val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
+ val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
+ view.staticDrawableColor = colors.staticDrawableColor(view.viewBounds, isColorized)
+ view.setDecorColor(colors.tint)
+ }
+ }
+}
+
+private val View.viewBounds: Rect
+ get() {
+ val tmpArray = intArrayOf(0, 0)
+ getLocationOnScreen(tmpArray)
+ return Rect(
+ /* left = */ tmpArray[0],
+ /* top = */ tmpArray[1],
+ /* right = */ left + width,
+ /* bottom = */ top + height,
+ )
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 835c059..b11eca2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -19,17 +19,13 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
-import com.android.systemui.util.ui.AnimatableEvent
-import com.android.systemui.util.ui.AnimatedValue
-import com.android.systemui.util.ui.toAnimatedValueFlow
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/** View-model for the row of notification icons displayed on the always-on display. */
@SysUISingleton
@@ -43,7 +39,7 @@
) {
/** Are changes to the icon container animated? */
- val animationsEnabled: Flow<Boolean> =
+ val areContainerChangesAnimated: Flow<Boolean> =
combine(
shadeInteractor.isShadeTouchable,
keyguardInteractor.isKeyguardVisible,
@@ -51,23 +47,21 @@
panelTouchesEnabled && isKeyguardVisible
}
- /** Should icons be rendered in "dozing" mode? */
- val isDozing: Flow<AnimatedValue<Boolean>> =
- keyguardTransitionInteractor.startedKeyguardTransitionStep
- // Determine if we're dozing based on the most recent transition
- .map { step: TransitionStep ->
- val isDozing = step.to == KeyguardState.AOD || step.to == KeyguardState.DOZING
- isDozing to step
- }
- // Only emit changes based on whether we've started or stopped dozing
- .distinctUntilChanged { (wasDozing, _), (isDozing, _) -> wasDozing != isDozing }
- // Determine whether we need to animate
- .map { (isDozing, step) ->
- val animate = step.to == KeyguardState.AOD || step.from == KeyguardState.AOD
- AnimatableEvent(isDozing, animate)
- }
- .distinctUntilChanged()
- .toAnimatedValueFlow()
+ /** Amount of a "white" tint to be applied to the icons. */
+ val tintAlpha: Flow<Float> =
+ combine(
+ keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).onStart { emit(0f) },
+ keyguardTransitionInteractor.transitionValue(KeyguardState.DOZING).onStart { emit(0f) },
+ ) { aodAmt, dozeAmt ->
+ aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
+ }
+
+ /** Are notification icons animated (ex: animated gif)? */
+ val areIconAnimationsEnabled: Flow<Boolean> =
+ keyguardTransitionInteractor.isFinishedInStateWhere {
+ // Don't animate icons when we're on AOD / dozing
+ it != KeyguardState.AOD && it != KeyguardState.DOZING
+ }
/** [NotificationIconsViewData] indicating which icons to display in the view. */
val icons: Flow<NotificationIconsViewData> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 6785da4..a1a0cca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
/** Binds the shared notification container to its view-model. */
@@ -32,39 +33,46 @@
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
controller: NotificationStackScrollLayoutController,
- ) {
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- viewModel.configurationBasedDimensions.collect {
- view.updateConstraints(
- useSplitShade = it.useSplitShade,
- marginStart = it.marginStart,
- marginTop = it.marginTop,
- marginEnd = it.marginEnd,
- marginBottom = it.marginBottom,
- )
+ ): DisposableHandle {
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.configurationBasedDimensions.collect {
+ view.updateConstraints(
+ useSplitShade = it.useSplitShade,
+ marginStart = it.marginStart,
+ marginTop = it.marginTop,
+ marginEnd = it.marginEnd,
+ marginBottom = it.marginBottom,
+ )
- controller.setOverExpansion(0f)
- controller.setOverScrollAmount(0)
- controller.updateFooter()
+ controller.setOverExpansion(0f)
+ controller.setOverScrollAmount(0)
+ controller.updateFooter()
+ }
}
- }
- launch {
- viewModel.maxNotifications.collect {
- controller.setMaxDisplayedNotifications(it)
+ launch {
+ viewModel.maxNotifications.collect {
+ controller.setMaxDisplayedNotifications(it)
+ }
}
- }
- launch {
- viewModel.position.collect {
- val animate = it.animate || controller.isAddOrRemoveAnimationPending()
- controller.updateTopPadding(it.top, animate)
+ launch {
+ viewModel.position.collect {
+ val animate = it.animate || controller.isAddOrRemoveAnimationPending()
+ controller.updateTopPadding(it.top, animate)
+ }
}
- }
- launch { viewModel.translationY.collect { controller.setTranslationY(it) } }
+ launch { viewModel.translationY.collect { controller.setTranslationY(it) } }
+ }
+ }
+
+ return object : DisposableHandle {
+ override fun dispose() {
+ disposableHandle.dispose()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 01f3b63..9e5fd95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -337,7 +337,9 @@
}
if (child instanceof StatusBarIconView) {
((StatusBarIconView) child).updateIconDimens();
- ((StatusBarIconView) child).setDozing(mDozing, false, 0);
+ if (!NotificationIconContainerRefactor.isEnabled()) {
+ ((StatusBarIconView) child).setDozing(mDozing, false, 0);
+ }
}
}
@@ -633,14 +635,16 @@
mChangingViewPositions = changingViewPositions;
}
- public void setDozing(boolean dozing, boolean fade, long delay) {
- setDozing(dozing, fade, delay, /* endRunnable= */ null);
+ public void setDozing(boolean dozing, boolean animate, long delay) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
+ setDozing(dozing, animate, delay, /* endRunnable= */ null);
}
- public void setDozing(boolean dozing, boolean fade, long delay,
+ private void setDozing(boolean dozing, boolean animate, long delay,
@Nullable Runnable endRunnable) {
+ NotificationIconContainerRefactor.assertInLegacyMode();
mDozing = dozing;
- mDisallowNextAnimation |= !fade;
+ mDisallowNextAnimation |= !animate;
final int childCount = getChildCount();
// Track all the child invocations of setDozing, invoking the top-level endRunnable once
// they have all completed.
@@ -657,7 +661,7 @@
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
if (view instanceof StatusBarIconView) {
- ((StatusBarIconView) view).setDozing(dozing, fade, delay, onChildCompleted);
+ ((StatusBarIconView) view).setDozing(dozing, animate, delay, onChildCompleted);
} else if (onChildCompleted != null) {
onChildCompleted.run();
}
@@ -780,15 +784,7 @@
StatusBarIconView icon = (StatusBarIconView) view;
boolean animate = false;
AnimationProperties animationProperties = null;
- final boolean isLowPriorityIconChange =
- (visibleState == StatusBarIconView.STATE_HIDDEN
- && icon.getVisibleState() == StatusBarIconView.STATE_DOT)
- || (visibleState == StatusBarIconView.STATE_DOT
- && icon.getVisibleState() == StatusBarIconView.STATE_HIDDEN);
- final boolean animationsAllowed = areAnimationsEnabled(icon)
- && !mDisallowNextAnimation
- && !noAnimations
- && !isLowPriorityIconChange;
+ final boolean animationsAllowed = animationsAllowed(icon);
if (animationsAllowed) {
if (justAdded || justReplaced) {
super.applyToView(icon);
@@ -879,6 +875,18 @@
needsCannedAnimation = false;
}
+ private boolean animationsAllowed(StatusBarIconView icon) {
+ final boolean isLowPriorityIconChange =
+ (visibleState == StatusBarIconView.STATE_HIDDEN
+ && icon.getVisibleState() == StatusBarIconView.STATE_DOT)
+ || (visibleState == StatusBarIconView.STATE_DOT
+ && icon.getVisibleState() == StatusBarIconView.STATE_HIDDEN);
+ return areAnimationsEnabled(icon)
+ && !mDisallowNextAnimation
+ && !noAnimations
+ && !isLowPriorityIconChange;
+ }
+
@Nullable
private Consumer<Property> getEndAction() {
if (mIsolatedIconAnimationEndRunnable == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/util/reference/ReferenceModule.kt b/packages/SystemUI/src/com/android/systemui/util/reference/ReferenceModule.kt
new file mode 100644
index 0000000..e7a91e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/reference/ReferenceModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.reference
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class ReferenceModule {
+ @Binds
+ abstract fun bindWeakReferenceFactory(impl: WeakReferenceFactoryImpl): WeakReferenceFactory
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/reference/WeakReferenceFactory.kt b/packages/SystemUI/src/com/android/systemui/util/reference/WeakReferenceFactory.kt
new file mode 100644
index 0000000..658f040
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/reference/WeakReferenceFactory.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.reference
+
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+interface WeakReferenceFactory {
+ fun <T> create(referent: T): WeakReference<T>
+}
+
+class WeakReferenceFactoryImpl @Inject constructor() : WeakReferenceFactory {
+ override fun <T> create(referent: T): WeakReference<T> {
+ return WeakReference(referent)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
new file mode 100644
index 0000000..455f986
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalMediaRepositoryImplTest : SysuiTestCase() {
+ @Mock private lateinit var mediaDataManager: MediaDataManager
+ @Mock private lateinit var mediaData: MediaData
+
+ private val mediaDataListenerCaptor: KotlinArgumentCaptor<MediaDataManager.Listener> by lazy {
+ KotlinArgumentCaptor(MediaDataManager.Listener::class.java)
+ }
+
+ private lateinit var mediaRepository: CommunalMediaRepository
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun mediaPlaying_defaultsToFalse() =
+ testScope.runTest {
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+ }
+
+ @Test
+ fun mediaPlaying_emitsInitialValue() =
+ testScope.runTest {
+ // Start with media available.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ val isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+ }
+
+ @Test
+ fun mediaPlaying_updatesWhenMediaDataLoaded() =
+ testScope.runTest {
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ // Initial value is false.
+ var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+
+ // Listener is added
+ verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+
+ // Change to media available and notify the listener.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+ mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
+
+ // mediaPlaying now returns true.
+ isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+ }
+
+ @Test
+ fun mediaPlaying_updatesWhenMediaDataRemoved() =
+ testScope.runTest {
+ // Start with media available.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
+
+ mediaRepository = CommunalMediaRepositoryImpl(mediaDataManager)
+
+ // Initial value is true.
+ var isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isTrue()
+
+ // Listener is added.
+ verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
+
+ // Change to media unavailable and notify the listener.
+ whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false)
+ mediaDataListenerCaptor.value.onMediaDataLoaded("key", null, mediaData)
+
+ // mediaPlaying now returns false.
+ isMediaPlaying = collectLastValue(mediaRepository.mediaPlaying)
+ runCurrent()
+ assertThat(isMediaPlaying()).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 5460a1b..08d54c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -24,6 +24,7 @@
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 @@
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 @@
tutorialRepository = withDeps.tutorialRepository
communalRepository = withDeps.communalRepository
+ mediaRepository = withDeps.mediaRepository
widgetRepository = withDeps.widgetRepository
smartspaceRepository = withDeps.smartspaceRepository
keyguardRepository = withDeps.keyguardRepository
@@ -237,6 +240,66 @@
}
@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 40f0ed3..288f3b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -33,6 +33,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.reference.FakeWeakReferenceFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -68,7 +69,8 @@
mExecutor,
/* overlayEnabled= */ true,
mFeatureFlags,
- FakeLogBuffer.Factory.Companion.create());
+ FakeLogBuffer.Factory.Companion.create(),
+ new FakeWeakReferenceFactory());
mLiveData = new ComplicationCollectionLiveData(mStateController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 365f67b..6d5cd49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.reference.FakeWeakReferenceFactory;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -63,6 +64,8 @@
final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ final FakeWeakReferenceFactory mWeakReferenceFactory = new FakeWeakReferenceFactory();
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -407,12 +410,36 @@
assertThat(stateController.getComplications()).contains(homeControlsComplication);
}
+ @Test
+ public void testCallbacksIgnoredWhenWeakReferenceCleared() {
+ final DreamOverlayStateController.Callback callback1 = Mockito.mock(
+ DreamOverlayStateController.Callback.class);
+ final DreamOverlayStateController.Callback callback2 = Mockito.mock(
+ DreamOverlayStateController.Callback.class);
+
+ final DreamOverlayStateController stateController = getDreamOverlayStateController(true);
+ stateController.addCallback(callback1);
+ stateController.addCallback(callback2);
+ mExecutor.runAllReady();
+
+ // Simulate callback1 getting GC'd by clearing the reference
+ mWeakReferenceFactory.clear(callback1);
+ stateController.setOverlayActive(true);
+ mExecutor.runAllReady();
+
+ // Callback2 should still be called, but never callback1
+ verify(callback1, never()).onStateChanged();
+ verify(callback2).onStateChanged();
+ assertThat(stateController.isOverlayActive()).isTrue();
+ }
+
private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) {
return new DreamOverlayStateController(
mExecutor,
overlayEnabled,
mFeatureFlags,
- mLogBuffer
+ mLogBuffer,
+ mWeakReferenceFactory
);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/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 8b572eb..0000000
--- 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 90fd652..4587ea6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -154,6 +154,7 @@
fun fingerprintEnrollmentChange() =
testScope.runTest {
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
runCurrent()
@@ -170,11 +171,34 @@
}
@Test
+ fun fingerprintEnabledStateChange() =
+ testScope.runTest {
+ createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
+ val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
+ runCurrent()
+
+ // start state
+ whenever(authController.isFingerprintEnrolled(anyInt())).thenReturn(true)
+ enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true)
+ assertThat(fingerprintAllowed()).isTrue()
+
+ // when biometrics are not enabled by settings
+ biometricsAreNotEnabledBySettings()
+ assertThat(fingerprintAllowed()).isFalse()
+
+ // when biometrics are enabled by settings
+ biometricsAreEnabledBySettings()
+ assertThat(fingerprintAllowed()).isTrue()
+ }
+
+ @Test
fun strongBiometricAllowedChange() =
testScope.runTest {
fingerprintIsEnrolled()
doNotDisableKeyguardAuthFeatures()
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val strongBiometricAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
@@ -197,7 +221,7 @@
createBiometricSettingsRepository()
val convenienceFaceAuthAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
doNotDisableKeyguardAuthFeatures()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(true, PRIMARY_USER_ID)
@@ -238,6 +262,7 @@
faceAuthIsNonStrongBiometric()
faceAuthIsEnrolled()
doNotDisableKeyguardAuthFeatures()
+ biometricsAreEnabledBySettings()
val convenienceBiometricAllowed = collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
runCurrent()
@@ -258,7 +283,7 @@
faceAuthIsEnrolled()
createBiometricSettingsRepository()
doNotDisableKeyguardAuthFeatures()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
runCurrent()
val convenienceBiometricAllowed by
@@ -291,6 +316,7 @@
testScope.runTest {
fingerprintIsEnrolled(PRIMARY_USER_ID)
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val fingerprintEnabledByDevicePolicy =
collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
@@ -316,7 +342,7 @@
createBiometricSettingsRepository()
val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
@@ -351,12 +377,18 @@
assertThat(faceAuthAllowed()).isTrue()
}
- private fun faceAuthIsEnabledByBiometricManager(userId: Int = PRIMARY_USER_ID) {
+ private fun biometricsAreEnabledBySettings(userId: Int = PRIMARY_USER_ID) {
verify(biometricManager, atLeastOnce())
.registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
biometricManagerCallback.value.onChanged(true, userId)
}
+ private fun biometricsAreNotEnabledBySettings(userId: Int = PRIMARY_USER_ID) {
+ verify(biometricManager, atLeastOnce())
+ .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture())
+ biometricManagerCallback.value.onChanged(false, userId)
+ }
+
@Test
fun faceEnrollmentStatusOfNewUserUponUserSwitch() =
testScope.runTest {
@@ -427,7 +459,7 @@
faceAuthIsEnrolled()
createBiometricSettingsRepository()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
doNotDisableKeyguardAuthFeatures()
mobileConnectionsRepository.isAnySimSecure.value = false
runCurrent()
@@ -454,7 +486,7 @@
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
mobileConnectionsRepository.isAnySimSecure.value = false
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
@@ -636,7 +668,7 @@
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsStrongBiometric()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
@@ -660,7 +692,7 @@
deviceIsInPostureThatSupportsFaceAuth()
doNotDisableKeyguardAuthFeatures()
faceAuthIsNonStrongBiometric()
- faceAuthIsEnabledByBiometricManager()
+ biometricsAreEnabledBySettings()
onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
onNonStrongAuthChanged(false, PRIMARY_USER_ID)
@@ -682,6 +714,7 @@
fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() =
testScope.runTest {
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val isFingerprintCurrentlyAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
@@ -723,6 +756,7 @@
fun fpAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFpIsStrong() =
testScope.runTest {
createBiometricSettingsRepository()
+ biometricsAreEnabledBySettings()
val isFingerprintCurrentlyAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
index 66ead14..5852bdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
@@ -25,10 +25,8 @@
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -43,20 +41,16 @@
private lateinit var underTest: KeyguardBlueprintRepository
@Mock lateinit var configurationRepository: ConfigurationRepository
@Mock lateinit var defaultLockscreenBlueprint: DefaultKeyguardBlueprint
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
- private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+ private val testScope = TestScope(StandardTestDispatcher())
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
whenever(defaultLockscreenBlueprint.id).thenReturn(DEFAULT)
- whenever(configurationRepository.onAnyConfigurationChange).thenReturn(configurationFlow)
underTest =
KeyguardBlueprintRepository(
configurationRepository,
setOf(defaultLockscreenBlueprint),
- testScope.backgroundScope,
)
}
@@ -64,20 +58,7 @@
fun testApplyBlueprint_DefaultLayout() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
- runCurrent()
underTest.applyBlueprint(defaultLockscreenBlueprint)
- runCurrent()
- assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
- }
- }
-
- @Test
- fun testConfigurationChange() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- runCurrent()
- configurationFlow.tryEmit(Unit)
- runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
@@ -86,9 +67,7 @@
fun testRefreshBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
- runCurrent()
underTest.refreshBlueprint()
- runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index b8a2e9d..9fe40d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -21,24 +21,77 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
+import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardBlueprintInteractorTest : SysuiTestCase() {
+ private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
private lateinit var underTest: KeyguardBlueprintInteractor
+ private lateinit var testScope: TestScope
+ @Mock private lateinit var splitShadeStateController: SplitShadeStateController
@Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- underTest = KeyguardBlueprintInteractor(keyguardBlueprintRepository)
+ testScope = TestScope(StandardTestDispatcher())
+ whenever(keyguardBlueprintRepository.configurationChange).thenReturn(configurationFlow)
+
+ underTest =
+ KeyguardBlueprintInteractor(
+ keyguardBlueprintRepository,
+ testScope.backgroundScope,
+ mContext,
+ splitShadeStateController,
+ )
+ }
+
+ @Test
+ fun testAppliesDefaultBlueprint() {
+ testScope.runTest {
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(false)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository)
+ .applyBlueprint(DefaultKeyguardBlueprint.Companion.DEFAULT)
+ }
+ }
+
+ @Test
+ fun testAppliesSplitShadeBlueprint() {
+ testScope.runTest {
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(true)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository)
+ .applyBlueprint(SplitShadeKeyguardBlueprint.Companion.ID)
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 2831053..43d70ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -86,7 +86,6 @@
defaultStatusViewSection,
defaultStatusBarViewSection,
defaultNSSLSection,
- splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
communalTutorialIndicatorSection,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 788cfbc..c2c33de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -45,9 +45,6 @@
import com.android.systemui.user.domain.UserDomainLayerModule
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.ui.isAnimating
-import com.android.systemui.util.ui.stopAnimating
-import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
@@ -129,7 +126,7 @@
transitionState = TransitionState.STARTED,
)
)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isFalse()
}
@@ -152,7 +149,7 @@
to = DozeStateModel.DOZE_AOD,
)
)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isFalse()
}
@@ -175,7 +172,7 @@
to = DozeStateModel.DOZE_PULSING,
)
)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isTrue()
}
@@ -196,7 +193,7 @@
)
)
whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isFalse()
}
@@ -217,7 +214,7 @@
)
)
whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isTrue()
}
@@ -235,7 +232,7 @@
transitionState = TransitionState.STARTED,
)
)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isTrue()
}
@@ -248,7 +245,7 @@
transitionState = TransitionState.STARTED,
)
)
- val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+ val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
keyguardRepository.setKeyguardShowing(true)
@@ -270,91 +267,151 @@
}
@Test
- fun isDozing_startAodTransition() =
+ fun tintAlpha_isZero_whenNotOnAodOrDozing() =
testComponent.runTest {
- val isDozing by collectLastValue(underTest.isDozing)
+ val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- transitionState = TransitionState.STARTED,
- )
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.GONE,
+ testScope,
)
runCurrent()
- assertThat(isDozing?.value).isTrue()
- assertThat(isDozing?.isAnimating).isTrue()
+ assertThat(tintAlpha).isZero()
}
@Test
- fun isDozing_startDozeTransition() =
+ fun tintAlpha_isOne_whenOnAod() =
testComponent.runTest {
- val isDozing by collectLastValue(underTest.isDozing)
+ val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope,
+ )
+ runCurrent()
+ assertThat(tintAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun tintAlpha_isOne_whenDozing() =
+ testComponent.runTest {
+ val tintAlpha by collectLastValue(underTest.tintAlpha)
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.DOZING,
+ testScope,
+ )
+ assertThat(tintAlpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun tintAlpha_isOne_whenTransitionFromAodToDoze() =
+ testComponent.runTest {
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope,
+ )
+ val tintAlpha by collectLastValue(underTest.tintAlpha)
+ runCurrent()
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
- from = KeyguardState.GONE,
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.AOD,
to = KeyguardState.DOZING,
- transitionState = TransitionState.STARTED,
+ value = 0f,
)
)
runCurrent()
- assertThat(isDozing?.value).isTrue()
- assertThat(isDozing?.isAnimating).isFalse()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.AOD,
+ to = KeyguardState.DOZING,
+ value = 0.5f,
+ )
+ )
+ runCurrent()
+
+ assertThat(tintAlpha).isEqualTo(1f)
}
@Test
- fun isDozing_startDozeToAodTransition() =
+ fun tintAlpha_isFraction_midTransitionToAod() =
testComponent.runTest {
- val isDozing by collectLastValue(underTest.isDozing)
+ val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
+
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
- from = KeyguardState.DOZING,
+ transitionState = TransitionState.STARTED,
+ from = KeyguardState.GONE,
to = KeyguardState.AOD,
- transitionState = TransitionState.STARTED,
+ value = 0f,
)
)
runCurrent()
- assertThat(isDozing?.value).isTrue()
- assertThat(isDozing?.isAnimating).isTrue()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ transitionState = TransitionState.RUNNING,
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0.5f,
+ )
+ )
+ runCurrent()
+
+ assertThat(tintAlpha).isEqualTo(0.5f)
}
@Test
- fun isNotDozing_startAodToGoneTransition() =
+ fun iconAnimationsEnabled_whenOnLockScreen() =
testComponent.runTest {
- val isDozing by collectLastValue(underTest.isDozing)
+ val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
runCurrent()
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.GONE,
- transitionState = TransitionState.STARTED,
- )
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
)
- runCurrent()
- assertThat(isDozing?.value).isFalse()
- assertThat(isDozing?.isAnimating).isTrue()
+
+ assertThat(iconAnimationsEnabled).isTrue()
}
@Test
- fun isDozing_stopAnimation() =
+ fun iconAnimationsDisabled_whenOnAod() =
testComponent.runTest {
- val isDozing by collectLastValue(underTest.isDozing)
+ val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
runCurrent()
- keyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.GONE,
- transitionState = TransitionState.STARTED,
- )
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ testScope,
)
+
+ assertThat(iconAnimationsEnabled).isFalse()
+ }
+
+ @Test
+ fun iconAnimationsDisabled_whenDozing() =
+ testComponent.runTest {
+ val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
runCurrent()
- assertThat(isDozing?.isAnimating).isEqualTo(true)
- isDozing?.stopAnimating()
- runCurrent()
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.DOZING,
+ testScope,
+ )
- assertThat(isDozing?.isAnimating).isEqualTo(false)
+ assertThat(iconAnimationsEnabled).isFalse()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 58d93c9..2f79955 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -294,6 +294,6 @@
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
when(mUsbPortStatus.getComplianceWarnings())
- .thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_OTHER});
+ .thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY});
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
new file mode 100644
index 0000000..3ab1b6c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeCommunalMediaRepository(
+ override val mediaPlaying: MutableStateFlow<Boolean> = MutableStateFlow(false)
+) : CommunalMediaRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index 6c3882f..0c821ea 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 @@
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 @@
return WithDependencies(
communalRepository,
widgetRepository,
+ mediaRepository,
smartspaceRepository,
tutorialRepository,
withDeps.keyguardRepository,
@@ -57,6 +60,7 @@
CommunalInteractor(
communalRepository,
widgetRepository,
+ mediaRepository,
smartspaceRepository,
withDeps.communalTutorialInteractor,
appWidgetHost,
@@ -67,6 +71,7 @@
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/util/reference/FakeWeakReferenceFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/reference/FakeWeakReferenceFactory.kt
new file mode 100644
index 0000000..f0a8fd0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/reference/FakeWeakReferenceFactory.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.reference
+
+import java.lang.ref.WeakReference
+
+class FakeWeakReferenceFactory : WeakReferenceFactory {
+ private val managedReferents = mutableListOf<WeakReference<*>>()
+
+ override fun <T> create(referent: T): WeakReference<T> {
+ val weakRef = WeakReference(referent)
+ managedReferents.add(weakRef)
+ return weakRef
+ }
+
+ /**
+ * Clears any [WeakReference] objects pointing to this object. If argument is null, clears all
+ * references.
+ */
+ fun <T> clear(referent: T) {
+ managedReferents.filter { it.get() == referent }.forEach { it.clear() }
+ }
+}
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index b37bbd6..ab678d9 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -20,3 +20,10 @@
description: "Mitigation for relayout issue"
bug: "294330426"
}
+
+flag {
+ name: "ignore_view_state_reset_to_empty"
+ namespace: "autofill"
+ description: "Mitigation for view state reset to empty causing no save dialog to show issue"
+ bug: "297976948"
+}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index afc0dd1..cd9c53b 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -83,6 +83,9 @@
}
],
"file_patterns": ["VpnManagerService\\.java"]
+ },
+ {
+ "name": "FrameworksNetTests"
}
],
"presubmit-large": [
@@ -124,9 +127,6 @@
},
{
"name": "CtsSuspendAppsTestCases"
- },
- {
- "name": "FrameworksNetTests"
}
]
}
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index b006ac8..a8cba53 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -35,11 +35,12 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
-import com.android.server.pm.UserManagerInternal;
import com.android.server.LocalServices;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
import libcore.io.IoUtils;
@@ -53,6 +54,9 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@@ -114,21 +118,26 @@
private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
- private static final int HEADER_SIZE = 8;
+ @VisibleForTesting
+ static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
private static final int PARTITION_TYPE_MARKER = 0x19901873;
/** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
/** Maximum size of the FRP credential handle that can be stored. */
- private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
+ @VisibleForTesting
+ static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
/**
* Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
*/
private static final int TEST_MODE_RESERVED_SIZE = 10000;
/** Maximum size of the Test Harness Mode data that can be stored. */
- private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
+ @VisibleForTesting
+ static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
+
// Limit to 100k as blocks larger than this might cause strain on Binder.
- private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
+ @VisibleForTesting
+ static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
public static final int DIGEST_SIZE_BYTES = 32;
private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
@@ -138,12 +147,12 @@
private final Context mContext;
private final String mDataBlockFile;
- private final boolean mIsRunningDSU;
+ private final boolean mIsFileBacked;
private final Object mLock = new Object();
private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
private int mAllowedUid = -1;
- private long mBlockDeviceSize;
+ private long mBlockDeviceSize = -1; // Load lazily
@GuardedBy("mLock")
private boolean mIsWritable = true;
@@ -151,13 +160,23 @@
public PersistentDataBlockService(Context context) {
super(context);
mContext = context;
- mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
- if (mIsRunningDSU) {
+ if (SystemProperties.getBoolean(GSI_RUNNING_PROP, false)) {
+ mIsFileBacked = true;
mDataBlockFile = GSI_SANDBOX;
} else {
+ mIsFileBacked = false;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
}
- mBlockDeviceSize = -1; // Load lazily
+ }
+
+ @VisibleForTesting
+ PersistentDataBlockService(Context context, boolean isFileBacked, String dataBlockFile,
+ long blockDeviceSize) {
+ super(context);
+ mContext = context;
+ mIsFileBacked = isFileBacked;
+ mDataBlockFile = dataBlockFile;
+ mBlockDeviceSize = blockDeviceSize;
}
private int getAllowedUid() {
@@ -212,6 +231,11 @@
super.onBootPhase(phase);
}
+ @VisibleForTesting
+ void setAllowedUid(int uid) {
+ mAllowedUid = uid;
+ }
+
private void formatIfOemUnlockEnabled() {
boolean enabled = doGetOemUnlockEnabled();
if (enabled) {
@@ -220,7 +244,7 @@
}
}
- SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
+ setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0");
}
private void enforceOemUnlockReadPermission() {
@@ -278,7 +302,7 @@
private long getBlockDeviceSize() {
synchronized (mLock) {
if (mBlockDeviceSize == -1) {
- if (mIsRunningDSU) {
+ if (mIsFileBacked) {
mBlockDeviceSize = MAX_DATA_BLOCK_SIZE;
} else {
mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
@@ -289,14 +313,26 @@
return mBlockDeviceSize;
}
- private long getFrpCredentialDataOffset() {
- return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
+ @VisibleForTesting
+ int getMaximumFrpDataSize() {
+ return (int) (getTestHarnessModeDataOffset() - DIGEST_SIZE_BYTES - HEADER_SIZE);
}
- private long getTestHarnessModeDataOffset() {
+ @VisibleForTesting
+ long getFrpCredentialDataOffset() {
+ return getOemUnlockDataOffset() - FRP_CREDENTIAL_RESERVED_SIZE;
+ }
+
+ @VisibleForTesting
+ long getTestHarnessModeDataOffset() {
return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
}
+ @VisibleForTesting
+ long getOemUnlockDataOffset() {
+ return getBlockDeviceSize() - 1;
+ }
+
private boolean enforceChecksumValidity() {
byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
@@ -385,7 +421,8 @@
return md.digest();
}
- private void formatPartitionLocked(boolean setOemUnlockEnabled) {
+ @VisibleForTesting
+ void formatPartitionLocked(boolean setOemUnlockEnabled) {
try {
FileChannel channel = getBlockOutputChannel();
@@ -448,10 +485,15 @@
Slog.e(TAG, "unable to access persistent partition", e);
return;
} finally {
- SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
+ setProperty(OEM_UNLOCK_PROP, enabled ? "1" : "0");
}
}
+ @VisibleForTesting
+ void setProperty(String name, String value) {
+ SystemProperties.set(name, value);
+ }
+
private boolean doGetOemUnlockEnabled() {
DataInputStream inputStream;
try {
@@ -483,6 +525,16 @@
private native long nativeGetBlockDeviceSize(String path);
private native int nativeWipe(String path);
+ @VisibleForTesting
+ IPersistentDataBlockService getInterfaceForTesting() {
+ return IPersistentDataBlockService.Stub.asInterface(mService);
+ }
+
+ @VisibleForTesting
+ PersistentDataBlockManagerInternal getInternalInterfaceForTesting() {
+ return mInternalService;
+ }
+
private final IBinder mService = new IPersistentDataBlockService.Stub() {
/**
@@ -588,7 +640,18 @@
enforceOemUnlockWritePermission();
synchronized (mLock) {
- int ret = nativeWipe(mDataBlockFile);
+ int ret;
+ if (mIsFileBacked) {
+ try {
+ Files.write(Paths.get(mDataBlockFile), new byte[MAX_DATA_BLOCK_SIZE],
+ StandardOpenOption.TRUNCATE_EXISTING);
+ ret = 0;
+ } catch (IOException e) {
+ ret = -1;
+ }
+ } else {
+ ret = nativeWipe(mDataBlockFile);
+ }
if (ret < 0) {
Slog.e(TAG, "failed to wipe persistent partition");
@@ -699,7 +762,7 @@
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
pw.println("mDataBlockFile: " + mDataBlockFile);
- pw.println("mIsRunningDSU: " + mIsRunningDSU);
+ pw.println("mIsFileBacked: " + mIsFileBacked);
pw.println("mInitDoneSignal: " + mInitDoneSignal);
pw.println("mAllowedUid: " + mAllowedUid);
pw.println("mBlockDeviceSize: " + mBlockDeviceSize);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 26e70c0..21284a0 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -410,7 +410,7 @@
* Map of all apex system services to the jar files they are contained in.
*/
@GuardedBy("mLock")
- private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
+ private final List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
/**
* Contains the list of {@code packageName}s of apks-in-apex for given
@@ -418,14 +418,14 @@
* difference between {@code packageName} and {@code apexModuleName}.
*/
@GuardedBy("mLock")
- private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
+ private final ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
/**
* Contains the list of {@code Exception}s that were raised when installing apk-in-apex
* inside {@code apexModuleName}.
*/
@GuardedBy("mLock")
- private Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
+ private final Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
/**
* An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index 5b32a94..a5bc2c3 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -201,7 +201,7 @@
protected static final boolean CACHE_VALID = true;
protected static final boolean CACHE_INVALID = false;
- protected AtomicBoolean mCacheValid = new AtomicBoolean(CACHE_INVALID);
+ protected final AtomicBoolean mCacheValid = new AtomicBoolean(CACHE_INVALID);
protected boolean isForceQueryable(int callingAppId) {
return mForceQueryable.contains(callingAppId);
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 9bca9f0..36677df 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -89,7 +89,7 @@
private static final long CANCELLATION_WAIT_CHECK_INTERVAL_MS = 200;
- private static ComponentName sDexoptServiceName =
+ private static final ComponentName sDexoptServiceName =
new ComponentName("android", BackgroundDexOptJobService.class.getName());
// Possible return codes of individual optimization steps.
@@ -179,7 +179,7 @@
private final long mDowngradeUnusedAppsThresholdInMillis;
- private List<PackagesUpdatedListener> mPackagesUpdatedListeners = new ArrayList<>();
+ private final List<PackagesUpdatedListener> mPackagesUpdatedListeners = new ArrayList<>();
private int mThermalStatusCutoff = THERMAL_CUTOFF_DEFAULT;
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/CrossProfileAppsService.java
index 486282a..0bd165e 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsService.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsService.java
@@ -21,7 +21,7 @@
import com.android.server.SystemService;
public class CrossProfileAppsService extends SystemService {
- private CrossProfileAppsServiceImpl mServiceImpl;
+ private final CrossProfileAppsServiceImpl mServiceImpl;
public CrossProfileAppsService(Context context) {
super(context);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 5f28e56..f1dc284 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -78,8 +78,8 @@
private final LocalService mLocalService = new LocalService();
- private Context mContext;
- private Injector mInjector;
+ private final Context mContext;
+ private final Injector mInjector;
public CrossProfileAppsServiceImpl(Context context) {
this(context, new InjectorImpl(context));
@@ -783,7 +783,7 @@
}
private static class InjectorImpl implements Injector {
- private Context mContext;
+ private final Context mContext;
public InjectorImpl(Context context) {
mContext = context;
diff --git a/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java b/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
index 72f3afc..3313e72 100644
--- a/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
+++ b/services/core/java/com/android/server/pm/CrossProfileDomainInfo.java
@@ -22,10 +22,10 @@
public final class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
- ResolveInfo mResolveInfo;
+ final ResolveInfo mResolveInfo;
int mHighestApprovalLevel;
@UserIdInt
- int mTargetUserId = UserHandle.USER_CURRENT; // default as current user
+ final int mTargetUserId;
CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel, @UserIdInt
int targetUserId) {
@@ -37,6 +37,7 @@
CrossProfileDomainInfo(ResolveInfo resolveInfo, int highestApprovalLevel) {
this.mResolveInfo = resolveInfo;
this.mHighestApprovalLevel = highestApprovalLevel;
+ this.mTargetUserId = UserHandle.USER_CURRENT; // default as current user
}
@Override
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java
index 51214fa..56e9c0a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilterHelper.java
@@ -25,11 +25,11 @@
* Helper class to manage {@link com.android.server.pm.CrossProfileIntentFilter}s.
*/
public class CrossProfileIntentFilterHelper {
- private Context mContext;
- private UserManagerInternal mUserManagerInternal;
- private Settings mSettings;
- private UserManagerService mUserManagerService;
- private PackageManagerTracedLock mLock;
+ private final Context mContext;
+ private final UserManagerInternal mUserManagerInternal;
+ private final Settings mSettings;
+ private final UserManagerService mUserManagerService;
+ private final PackageManagerTracedLock mLock;
public CrossProfileIntentFilterHelper(Settings settings, UserManagerService userManagerService,
PackageManagerTracedLock lock, UserManagerInternal userManagerInternal,
diff --git a/services/core/java/com/android/server/pm/CrossProfileResolver.java b/services/core/java/com/android/server/pm/CrossProfileResolver.java
index a8da818..1d38bbb 100644
--- a/services/core/java/com/android/server/pm/CrossProfileResolver.java
+++ b/services/core/java/com/android/server/pm/CrossProfileResolver.java
@@ -36,8 +36,8 @@
*/
public abstract class CrossProfileResolver {
- protected ComponentResolverApi mComponentResolver;
- protected UserManagerService mUserManager;
+ protected final ComponentResolverApi mComponentResolver;
+ protected final UserManagerService mUserManager;
public CrossProfileResolver(ComponentResolverApi componentResolver,
UserManagerService userManager) {
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 29322e2..888e1c2 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -50,7 +50,8 @@
private final HandlerThread mThread;
private final Handler mHandler;
private final DataLoaderManagerBinderService mBinderService;
- private SparseArray<DataLoaderServiceConnection> mServiceConnections = new SparseArray<>();
+ private final SparseArray<DataLoaderServiceConnection> mServiceConnections =
+ new SparseArray<>();
public DataLoaderManagerService(Context context) {
super(context);
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
index dc5915d..db05dd3 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -75,10 +75,10 @@
}
static final class Builder {
- private WatchedIntentFilter mFilter = new WatchedIntentFilter();
- private int mFlags;
- private @Direction int mDirection;
- private boolean mLetsPersonalDataIntoProfile;
+ private final WatchedIntentFilter mFilter = new WatchedIntentFilter();
+ private final int mFlags;
+ private final @Direction int mDirection;
+ private final boolean mLetsPersonalDataIntoProfile;
Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
mDirection = direction;
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index dd96a2b..cc6bb00 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -67,7 +67,8 @@
// The list of instruction sets supported by this app. This is currently
// only used during the rmdex() phase to clean up resources. We can get rid of this
// if we move dex files under the common app path.
- @Nullable String[] mInstructionSets;
+ @Nullable
+ final String[] mInstructionSets;
InstallArgs(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
int installFlags, int developmentInstallFlags, InstallSource installSource,
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index be4fb5c..fc83120 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -153,7 +153,7 @@
private int[] mUpdateBroadcastInstantUserIds = EMPTY_INT_ARRAY;
@NonNull
- private ArrayList<String> mWarnings = new ArrayList<>();
+ private final ArrayList<String> mWarnings = new ArrayList<>();
// New install
InstallRequest(InstallingSession params) {
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index ca8dc29..187cada 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -68,7 +68,7 @@
final MoveInfo mMoveInfo;
final IPackageInstallObserver2 mObserver;
int mInstallFlags;
- int mDevelopmentInstallFlags;
+ final int mDevelopmentInstallFlags;
@NonNull
final InstallSource mInstallSource;
final String mVolumeUuid;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8b82a1c..3f4cc4a 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -233,7 +233,8 @@
final LauncherAppsServiceInternal mInternal;
@NonNull
- private final RemoteCallbackList<IDumpCallback> mDumpCallbacks = new RemoteCallbackList<>();
+ private final RemoteCallbackList<IDumpCallback> mDumpCallbacks =
+ new RemoteCallbackList<>();
public LauncherAppsImpl(Context context) {
mContext = context;
@@ -2374,8 +2375,8 @@
class PackageLoadingProgressCallback extends
PackageManagerInternal.InstalledLoadingProgressCallback {
- private String mPackageName;
- private UserHandle mUser;
+ private final String mPackageName;
+ private final UserHandle mUser;
PackageLoadingProgressCallback(String packageName, UserHandle user) {
super(mCallbackHandler);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a4d8632..98eee4d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1867,7 +1867,8 @@
}
static class ParentChildSessionMap {
- private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap;
+ private final TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>>
+ mSessionMap;
private final Comparator<PackageInstallerSession> mSessionCreationComparator =
Comparator.comparingLong(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1be28ca..4b466be 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -491,7 +491,7 @@
@GuardedBy("mLock")
private SigningDetails mSigningDetails;
@GuardedBy("mLock")
- private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
+ private final SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
@GuardedBy("mLock")
private int mParentSessionId;
@@ -535,7 +535,7 @@
}
@GuardedBy("mLock")
- private ArraySet<FileEntry> mFiles = new ArraySet<>();
+ private final ArraySet<FileEntry> mFiles = new ArraySet<>();
static class PerFileChecksum {
private final Checksum[] mChecksums;
@@ -556,7 +556,7 @@
}
@GuardedBy("mLock")
- private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
+ private final ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
@GuardedBy("mLock")
private boolean mSessionApplied;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 9428ef6..655b9c9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -60,7 +60,7 @@
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
public InstantAppRegistry instantAppRegistry;
- public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
+ public final ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
public InstantAppResolverConnection instantAppResolverConnection;
public ComponentName instantAppResolverSettingsComponent;
public boolean isPreNmr1Upgrade;
@@ -118,7 +118,7 @@
public SuspendPackageHelper suspendPackageHelper;
public DistractingPackageHelper distractingPackageHelper;
public StorageEventHelper storageEventHelper;
- public Set<String> initialNonStoppedSystemPackages = new ArraySet<>();
+ public final Set<String> initialNonStoppedSystemPackages = new ArraySet<>();
public boolean shouldStopSystemPackagesByDefault;
public FreeStorageHelper freeStorageHelper;
public PackageMonitorCallbackHelper packageMonitorCallbackHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d4abad8..6f45d2b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2364,7 +2364,7 @@
private boolean mSuccess = false;
private int mErrCode = -1;
private ParcelFileDescriptor mProfileReadFd = null;
- private CountDownLatch mDoneSignal = new CountDownLatch(1);
+ private final CountDownLatch mDoneSignal = new CountDownLatch(1);
@Override
public void onSuccess(ParcelFileDescriptor profileReadFd) {
@@ -5186,7 +5186,7 @@
private static class LocalIntentReceiver {
private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
- private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index a09e713..c829e1c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -148,7 +148,8 @@
private final byte[] mData;
private final String mSalt;
- private static AtomicLong sGlobalSalt = new AtomicLong((new SecureRandom()).nextLong());
+ private static final AtomicLong sGlobalSalt =
+ new AtomicLong((new SecureRandom()).nextLong());
private static Long nextGlobalSalt() {
return sGlobalSalt.incrementAndGet();
}
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 5507e7c..18caafd 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -48,7 +48,7 @@
public final int mMatch;
public final ComponentName mComponent;
// Whether this is to be the one that's always chosen. If false, it's the most recently chosen.
- public boolean mAlways;
+ public final boolean mAlways;
final String[] mSetPackages;
final String[] mSetClasses;
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index cf5aa7b..e667bfe 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -68,12 +68,12 @@
// All policy stanzas read from mac_permissions.xml. This is also the lock
// to synchronize access during policy load and access attempts.
- private static List<Policy> sPolicies = new ArrayList<>();
+ private static final List<Policy> sPolicies = new ArrayList<>();
/** Whether or not the policy files have been read */
private static boolean sPolicyRead;
/** Required MAC permissions files */
- private static List<File> sMacPermissions = new ArrayList<>();
+ private static final List<File> sMacPermissions = new ArrayList<>();
private static final String DEFAULT_SEINFO = "default";
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 440823c..6338965 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3944,9 +3944,9 @@
mDisabledSysPackages.put(name, ps);
}
- private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
- private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
- private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
+ private static final int PRE_M_APP_INFO_FLAG_HIDDEN = 1 << 27;
+ private static final int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1 << 28;
+ private static final int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1 << 30;
private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users,
ArrayMap<String, Long> originalFirstInstallTimes)
@@ -5846,7 +5846,7 @@
@GuardedBy("mLock")
// Tracking the mutations that haven't yet been written to legacy state.
// This avoids unnecessary work when writing settings for multiple users.
- private AtomicBoolean mIsLegacyPermissionStateStale = new AtomicBoolean(false);
+ private final AtomicBoolean mIsLegacyPermissionStateStale = new AtomicBoolean(false);
@GuardedBy("mLock")
// The mapping keys are user ids.
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 8667888..12115af 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -53,7 +53,7 @@
protected ShortcutUser mShortcutUser;
@GuardedBy("mLock")
- protected ShortcutBitmapSaver mShortcutBitmapSaver;
+ protected final ShortcutBitmapSaver mShortcutBitmapSaver;
protected final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c6aba2a..3adeb4b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -296,24 +296,26 @@
private final Object mNonPersistentUsersLock = new Object();
private final Object mWtfLock = new Object();
- private static List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
+ private static final List<ResolveInfo> EMPTY_RESOLVE_INFO = new ArrayList<>(0);
// Temporarily reverted to anonymous inner class form due to: b/32554459
- private static Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED = new Predicate<ResolveInfo>() {
- public boolean test(ResolveInfo ri) {
- return !ri.activityInfo.exported;
- }
- };
+ private static final Predicate<ResolveInfo> ACTIVITY_NOT_EXPORTED =
+ new Predicate<ResolveInfo>() {
+ public boolean test(ResolveInfo ri) {
+ return !ri.activityInfo.exported;
+ }
+ };
- private static Predicate<ResolveInfo> ACTIVITY_NOT_INSTALLED = (ri) ->
+ private static final Predicate<ResolveInfo> ACTIVITY_NOT_INSTALLED = (ri) ->
!isInstalled(ri.activityInfo);
// Temporarily reverted to anonymous inner class form due to: b/32554459
- private static Predicate<PackageInfo> PACKAGE_NOT_INSTALLED = new Predicate<PackageInfo>() {
- public boolean test(PackageInfo pi) {
- return !isInstalled(pi);
- }
- };
+ private static final Predicate<PackageInfo> PACKAGE_NOT_INSTALLED =
+ new Predicate<PackageInfo>() {
+ public boolean test(PackageInfo pi) {
+ return !isInstalled(pi);
+ }
+ };
private final Handler mHandler;
@@ -4656,8 +4658,8 @@
private boolean mDumpFiles = false;
private boolean mDumpDetails = true;
- private List<Pattern> mPackagePatterns = new ArrayList<>();
- private List<Integer> mUsers = new ArrayList<>();
+ private final List<Pattern> mPackagePatterns = new ArrayList<>();
+ private final List<Integer> mUsers = new ArrayList<>();
void addPackageRegex(String regex) {
mPackagePatterns.add(Pattern.compile(regex));
diff --git a/services/core/java/com/android/server/pm/SnapshotStatistics.java b/services/core/java/com/android/server/pm/SnapshotStatistics.java
index e04a1e5..31541d0 100644
--- a/services/core/java/com/android/server/pm/SnapshotStatistics.java
+++ b/services/core/java/com/android/server/pm/SnapshotStatistics.java
@@ -157,12 +157,12 @@
private static class BinMap {
// The number of bins
- private int mCount;
+ private final int mCount;
// The maximum mapped value. Values at or above this are mapped to the
// top bin.
- private int mMaxBin;
+ private final int mMaxBin;
// A copy of the original key
- private int[] mUserKey;
+ private final int[] mUserKey;
/**
* Create a bin map. The input is an array of integers, which must be
@@ -232,13 +232,13 @@
* The build-time histogram. The total number of rebuilds is the sum over the
* histogram entries.
*/
- public int[] mTimes;
+ public final int[] mTimes;
/**
* The reuse histogram. The total number of snapshot uses is the sum over the
* histogram entries.
*/
- public int[] mUsed;
+ public final int[] mUsed;
/**
* The total number of rebuilds. This could be computed by summing over the use
@@ -477,12 +477,12 @@
/**
* Long statistics. These roll over approximately one day.
*/
- private Stats[] mLong;
+ private final Stats[] mLong;
/**
* Short statistics. These roll over approximately every minute;
*/
- private Stats[] mShort;
+ private final Stats[] mShort;
/**
* The time of last logging to the FrameworkStatsLog.
diff --git a/services/core/java/com/android/server/pm/UserJourneyLogger.java b/services/core/java/com/android/server/pm/UserJourneyLogger.java
index 895edce..651578d 100644
--- a/services/core/java/com/android/server/pm/UserJourneyLogger.java
+++ b/services/core/java/com/android/server/pm/UserJourneyLogger.java
@@ -586,7 +586,7 @@
public final long mSessionId;
@UserJourney
public final int mJourney;
- public long mStartTimeInMills;
+ public final long mStartTimeInMills;
@VisibleForTesting
public UserJourneySession(long sessionId, @UserJourney int journey) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6e738da..78c13f8 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -119,10 +119,10 @@
private final int mCriticalBatteryLevel;
// Possible outcomes of a dex search.
- private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
- private static int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk
- private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
- private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
+ private static final int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
+ private static final int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk
+ private static final int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
+ private static final int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
public DexManager(Context context, PackageDexOptimizer pdo, Installer installer,
Object installLock, DynamicCodeLogger dynamicCodeLogger) {
@@ -959,8 +959,8 @@
* Convenience class to store ownership search results.
*/
private class DexSearchResult {
- private String mOwningPackageName;
- private int mOutcome;
+ private final String mOwningPackageName;
+ private final int mOutcome;
public DexSearchResult(String owningPackageName, int outcome) {
this.mOwningPackageName = owningPackageName;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index d3a64bb..2ab7db4 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -49,7 +49,7 @@
public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();
@NonNull
- private File mCacheDir;
+ private final File mCacheDir;
public PackageCacher(@NonNull File cacheDir) {
this.mCacheDir = cacheDir;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 9e20805..c26cf1c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -1132,7 +1132,7 @@
*/
public static class CachedApplicationInfoGenerator {
// Map from a package name to the corresponding app info.
- private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
+ private final ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
/**
* {@link PackageInfoUtils#generateApplicationInfo} with a cache.
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index d82a500..3b10c7f 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -96,19 +96,19 @@
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
- private ThreadLocal<ApplicationInfo> mSharedAppInfo =
+ private final ThreadLocal<ApplicationInfo> mSharedAppInfo =
ThreadLocal.withInitial(() -> {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = -1; // Not a valid UID since the app will not be installed yet
return appInfo;
});
- private ThreadLocal<ParseTypeImpl> mSharedResult;
+ private final ThreadLocal<ParseTypeImpl> mSharedResult;
@Nullable
protected PackageCacher mCacher;
- private ParsingPackageUtils parsingUtils;
+ private final ParsingPackageUtils parsingUtils;
public PackageParser2(String[] separateProcesses, DisplayMetrics displayMetrics,
@Nullable File cacheDir, @NonNull Callback callback) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 056aae4..75dd67d 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -113,21 +113,21 @@
private static final SparseArray<int[]> EMPTY_INT_ARRAY_SPARSE_ARRAY = new SparseArray<>();
private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
(first, second) -> Integer.compare(second.getOrder(), first.getOrder());
- public static Parcelling.BuiltIn.ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(
+ public static final Parcelling.BuiltIn.ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(
Parcelling.BuiltIn.ForBoolean.class);
- public static ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
+ public static final ForInternedString sForInternedString = Parcelling.Cache.getOrCreate(
ForInternedString.class);
- public static Parcelling.BuiltIn.ForInternedStringArray sForInternedStringArray = Parcelling.Cache.getOrCreate(
- Parcelling.BuiltIn.ForInternedStringArray.class);
- public static Parcelling.BuiltIn.ForInternedStringList sForInternedStringList = Parcelling.Cache.getOrCreate(
- Parcelling.BuiltIn.ForInternedStringList.class);
- public static Parcelling.BuiltIn.ForInternedStringValueMap sForInternedStringValueMap =
+ public static final Parcelling.BuiltIn.ForInternedStringArray sForInternedStringArray =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringArray.class);
+ public static final Parcelling.BuiltIn.ForInternedStringList sForInternedStringList =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringList.class);
+ public static final Parcelling.BuiltIn.ForInternedStringValueMap sForInternedStringValueMap =
Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringValueMap.class);
- public static Parcelling.BuiltIn.ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(
- Parcelling.BuiltIn.ForStringSet.class);
- public static Parcelling.BuiltIn.ForInternedStringSet sForInternedStringSet =
+ public static final Parcelling.BuiltIn.ForStringSet sForStringSet =
+ Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
+ public static final Parcelling.BuiltIn.ForInternedStringSet sForInternedStringSet =
Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForInternedStringSet.class);
- protected static ParsingUtils.StringPairListParceler sForIntentInfoPairs =
+ protected static final ParsingUtils.StringPairListParceler sForIntentInfoPairs =
new ParsingUtils.StringPairListParceler();
protected int versionCode;
protected int versionCodeMajor;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index d6a7dc6..6f6bb45 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1748,14 +1748,14 @@
*/
private class DelayingPackageManagerCache extends PackageManagerWrapper {
/** uid -> permission -> isGranted, flags */
- private SparseArray<ArrayMap<String, PermissionState>> mDelayedPermissionState =
+ private final SparseArray<ArrayMap<String, PermissionState>> mDelayedPermissionState =
new SparseArray<>();
/** userId -> context */
- private SparseArray<Context> mUserContexts = new SparseArray<>();
+ private final SparseArray<Context> mUserContexts = new SparseArray<>();
/** Permission name -> info */
- private ArrayMap<String, PermissionInfo> mPermissionInfos = new ArrayMap<>();
+ private final ArrayMap<String, PermissionInfo> mPermissionInfos = new ArrayMap<>();
/** Package name -> info */
- private ArrayMap<String, PackageInfo> mPackageInfos = new ArrayMap<>();
+ private final ArrayMap<String, PackageInfo> mPackageInfos = new ArrayMap<>();
/**
* Apply the cached state
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
index 70986c3..4c831d3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionImpl.java
@@ -41,7 +41,8 @@
public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedPermission,
Parcelable {
- private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
+ private static final ForStringSet sForStringSet =
+ Parcelling.Cache.getOrCreate(ForStringSet.class);
@Nullable
private String backgroundPermission;
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 8240c47..061698a 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -328,11 +328,11 @@
return input.success(pkg);
}
- private String[] mSeparateProcesses;
- private DisplayMetrics mDisplayMetrics;
+ private final String[] mSeparateProcesses;
+ private final DisplayMetrics mDisplayMetrics;
@NonNull
- private List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
- private Callback mCallback;
+ private final List<PermissionManager.SplitPermissionInfo> mSplitPermissionInfos;
+ private final Callback mCallback;
public ParsingPackageUtils(String[] separateProcesses, DisplayMetrics displayMetrics,
@NonNull List<PermissionManager.SplitPermissionInfo> splitPermissions,
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index fac681a..0ceda42 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -951,7 +951,7 @@
extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
@NonNull
- private UserNeedsBadgingCache mUserNeedsBadging;
+ private final UserNeedsBadgingCache mUserNeedsBadging;
// Default constructor
ActivityIntentResolver(@NonNull UserManagerService userManager,
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
index 9115775..80cde73 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -68,7 +68,7 @@
protected ArrayMap<String, ParsedProvider> mProvidersByAuthority;
@NonNull
- protected UserManagerService mUserManager;
+ protected final UserManagerService mUserManager;
protected ComponentResolverBase(@NonNull UserManagerService userManager) {
mUserManager = userManager;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 11f62e9..3d4d4ec 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -2005,9 +2005,9 @@
private static class GetAttachedResult {
@Nullable
- private DomainVerificationPkgState mPkgState;
+ private final DomainVerificationPkgState mPkgState;
- private int mErrorCode;
+ private final int mErrorCode;
GetAttachedResult(@Nullable DomainVerificationPkgState pkgState, int errorCode) {
mPkgState = pkgState;
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
index 6c56360..d71dbbb 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationPkgState.java
@@ -40,7 +40,7 @@
private final String mPackageName;
@NonNull
- private UUID mId;
+ private final UUID mId;
/**
* Whether or not the package declares any autoVerify domains. This is separate from an empty
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index a388932..b8e581f 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -592,6 +592,10 @@
public void dumpCpuPowerBracketsLocked(PrintWriter pw) {
ensureInitialized();
+ if (mLayout == null) {
+ return;
+ }
+
pw.println("CPU power brackets; cluster/freq in MHz(avg current in mA):");
for (int bracket = 0; bracket < mLayout.getCpuPowerBracketCount(); bracket++) {
pw.print(" ");
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index dc05462..80427b3 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -25,7 +25,7 @@
mThiz = env->NewWeakGlobalRef(thiz);
mTvInput = tvInput;
mLooper = looper;
- mTvInputCallback = ::ndk::SharedRefBase::make<TvInputCallback>(this);
+ mTvInputCallback = std::shared_ptr<TvInputCallbackWrapper>(new TvInputCallbackWrapper(this));
mTvInput->setCallback(mTvInputCallback);
}
@@ -443,18 +443,23 @@
}
}
-JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
+JTvInputHal::TvInputCallbackWrapper::TvInputCallbackWrapper(JTvInputHal* hal) {
+ aidlTvInputCallback = ::ndk::SharedRefBase::make<AidlTvInputCallback>(hal);
+ hidlTvInputCallback = sp<HidlTvInputCallback>::make(hal);
+}
+
+JTvInputHal::AidlTvInputCallback::AidlTvInputCallback(JTvInputHal* hal) {
mHal = hal;
}
-::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notify(const AidlTvInputEvent& event) {
+::ndk::ScopedAStatus JTvInputHal::AidlTvInputCallback::notify(const AidlTvInputEvent& event) {
mHal->mLooper->sendMessage(new NotifyHandler(mHal,
TvInputEventWrapper::createEventWrapper(event)),
static_cast<int>(event.type));
return ::ndk::ScopedAStatus::ok();
}
-::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notifyTvMessageEvent(
+::ndk::ScopedAStatus JTvInputHal::AidlTvInputCallback::notifyTvMessageEvent(
const AidlTvMessageEvent& event) {
const std::string DEVICE_ID_SUBTYPE = "device_id";
::ndk::ScopedAStatus status = ::ndk::ScopedAStatus::ok();
@@ -487,11 +492,14 @@
: mIsHidl(false), mAidlTvInput(aidlTvInput) {}
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback(
- const std::shared_ptr<TvInputCallback>& in_callback) {
+ const std::shared_ptr<TvInputCallbackWrapper>& in_callback) {
if (mIsHidl) {
- return hidlSetCallback(in_callback);
+ in_callback->aidlTvInputCallback = nullptr;
+ return hidlSetCallback(in_callback == nullptr ? nullptr : in_callback->hidlTvInputCallback);
} else {
- return mAidlTvInput->setCallback(in_callback);
+ in_callback->hidlTvInputCallback = nullptr;
+ return mAidlTvInput->setCallback(in_callback == nullptr ? nullptr
+ : in_callback->aidlTvInputCallback);
}
}
diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h
index 6026a10..2ef94ac 100644
--- a/services/core/jni/tvinput/JTvInputHal.h
+++ b/services/core/jni/tvinput/JTvInputHal.h
@@ -168,23 +168,39 @@
JTvInputHal* mHal;
};
- class TvInputCallback : public HidlITvInputCallback, public BnTvInputCallback {
+ class AidlTvInputCallback : public BnTvInputCallback {
public:
- explicit TvInputCallback(JTvInputHal* hal);
+ explicit AidlTvInputCallback(JTvInputHal* hal);
::ndk::ScopedAStatus notify(const AidlTvInputEvent& event) override;
::ndk::ScopedAStatus notifyTvMessageEvent(const AidlTvMessageEvent& event) override;
+
+ private:
+ JTvInputHal* mHal;
+ };
+
+ class HidlTvInputCallback : public HidlITvInputCallback {
+ public:
+ explicit HidlTvInputCallback(JTvInputHal* hal);
Return<void> notify(const HidlTvInputEvent& event) override;
private:
JTvInputHal* mHal;
};
+ class TvInputCallbackWrapper {
+ public:
+ explicit TvInputCallbackWrapper(JTvInputHal* hal);
+ std::shared_ptr<AidlTvInputCallback> aidlTvInputCallback;
+ sp<HidlTvInputCallback> hidlTvInputCallback;
+ };
+
class ITvInputWrapper {
public:
ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput);
ITvInputWrapper(sp<HidlITvInput>& hidlTvInput);
- ::ndk::ScopedAStatus setCallback(const std::shared_ptr<TvInputCallback>& in_callback);
+ ::ndk::ScopedAStatus setCallback(
+ const std::shared_ptr<TvInputCallbackWrapper>& in_callback);
::ndk::ScopedAStatus getStreamConfigurations(int32_t in_deviceId,
std::vector<AidlTvStreamConfig>* _aidl_return);
::ndk::ScopedAStatus openStream(int32_t in_deviceId, int32_t in_streamId,
@@ -198,7 +214,7 @@
::ndk::ScopedAStatus getAidlInterfaceVersion(int32_t* _aidl_return);
private:
- ::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback);
+ ::ndk::ScopedAStatus hidlSetCallback(const sp<HidlTvInputCallback>& in_callback);
::ndk::ScopedAStatus hidlGetStreamConfigurations(
int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return);
::ndk::ScopedAStatus hidlOpenStream(int32_t in_deviceId, int32_t in_streamId,
@@ -229,7 +245,7 @@
KeyedVector<int, KeyedVector<int, Connection> > mConnections;
std::shared_ptr<ITvInputWrapper> mTvInput;
- std::shared_ptr<TvInputCallback> mTvInputCallback;
+ std::shared_ptr<TvInputCallbackWrapper> mTvInputCallback;
};
} // namespace android
diff --git a/services/core/jni/tvinput/TvInputHal_hidl.cpp b/services/core/jni/tvinput/TvInputHal_hidl.cpp
index 37cf844..cdd9266 100644
--- a/services/core/jni/tvinput/TvInputHal_hidl.cpp
+++ b/services/core/jni/tvinput/TvInputHal_hidl.cpp
@@ -59,7 +59,11 @@
return event;
}
-Return<void> JTvInputHal::TvInputCallback::notify(const HidlTvInputEvent& event) {
+JTvInputHal::HidlTvInputCallback::HidlTvInputCallback(JTvInputHal* hal) {
+ mHal = hal;
+}
+
+Return<void> JTvInputHal::HidlTvInputCallback::notify(const HidlTvInputEvent& event) {
mHal->mLooper->sendMessage(new NotifyHandler(mHal,
TvInputEventWrapper::createEventWrapper(event)),
static_cast<int>(event.type));
@@ -70,9 +74,8 @@
: mIsHidl(true), mHidlTvInput(hidlTvInput) {}
::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlSetCallback(
- const std::shared_ptr<TvInputCallback>& in_callback) {
- mHidlTvInput->setCallback(in_callback == nullptr ? nullptr
- : sp<TvInputCallback>(in_callback.get()));
+ const sp<HidlTvInputCallback>& in_callback) {
+ mHidlTvInput->setCallback(in_callback);
return ::ndk::ScopedAStatus::ok();
}
diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
new file mode 100644
index 0000000..f537efd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pdb;
+
+import static com.android.server.pdb.PersistentDataBlockService.DIGEST_SIZE_BYTES;
+import static com.android.server.pdb.PersistentDataBlockService.MAX_DATA_BLOCK_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.MAX_FRP_CREDENTIAL_HANDLE_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.MAX_TEST_MODE_DATA_SIZE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertThrows;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.service.persistentdata.IPersistentDataBlockService;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
+
+@RunWith(JUnitParamsRunner.class)
+public class PersistentDataBlockServiceTest {
+ private static final String TAG = "PersistentDataBlockServiceTest";
+
+ private static final byte[] SMALL_DATA = "data to write".getBytes();
+ private static final byte[] ANOTHER_SMALL_DATA = "something else".getBytes();
+
+ private Context mContext;
+ private PersistentDataBlockService mPdbService;
+ private IPersistentDataBlockService mInterface;
+ private PersistentDataBlockManagerInternal mInternalInterface;
+ private File mDataBlockFile;
+ private String mOemUnlockPropertyValue;
+
+ @Mock private UserManager mUserManager;
+
+ private class FakePersistentDataBlockService extends PersistentDataBlockService {
+ FakePersistentDataBlockService(Context context, String dataBlockFile,
+ long blockDeviceSize) {
+ super(context, /* isFileBacked */ true, dataBlockFile, blockDeviceSize);
+ }
+
+ @Override
+ void setProperty(String key, String value) {
+ // Override to capture the value instead of actually setting the property.
+ assertThat(key).isEqualTo("sys.oem_unlock_allowed");
+ mOemUnlockPropertyValue = value;
+ }
+ }
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDataBlockFile = mTemporaryFolder.newFile();
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ mPdbService = new FakePersistentDataBlockService(mContext, mDataBlockFile.getPath(),
+ /* blockDeviceSize */ -1);
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+ mPdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false);
+ mInterface = mPdbService.getInterfaceForTesting();
+ mInternalInterface = mPdbService.getInternalInterfaceForTesting();
+
+ when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+ }
+
+ abstract static class Block {
+ public PersistentDataBlockService service;
+
+ abstract int write(byte[] data) throws RemoteException;
+ abstract byte[] read() throws RemoteException;
+ }
+
+ /**
+ * Configuration for parameterizing tests, including the block name, maximum block size, and
+ * a block implementation for the read/write operations.
+ */
+ public Object[][] getTestParametersForBlocks() {
+ return new Object[][] {
+ {
+ new Block() {
+ @Override public int write(byte[] data) throws RemoteException {
+ return service.getInterfaceForTesting().write(data);
+ }
+
+ @Override public byte[] read() throws RemoteException {
+ return service.getInterfaceForTesting().read();
+ }
+ },
+ },
+ {
+ new Block() {
+ @Override public int write(byte[] data) {
+ service.getInternalInterfaceForTesting().setFrpCredentialHandle(data);
+ // The written size isn't returned. Pretend it's fully written in the
+ // test for now.
+ return data.length;
+ }
+
+ @Override public byte[] read() {
+ return service.getInternalInterfaceForTesting().getFrpCredentialHandle();
+ }
+ },
+ },
+ {
+ new Block() {
+ @Override public int write(byte[] data) {
+ service.getInternalInterfaceForTesting().setTestHarnessModeData(data);
+ // The written size isn't returned. Pretend it's fully written in the
+ // test for now.
+ return data.length;
+ }
+
+ @Override public byte[] read() {
+ return service.getInternalInterfaceForTesting().getTestHarnessModeData();
+ }
+ },
+ },
+ };
+ }
+
+ @Test
+ @Parameters(method = "getTestParametersForBlocks")
+ public void writeThenRead(Block block) throws Exception {
+ block.service = mPdbService;
+ assertThat(block.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+ assertThat(block.read()).isEqualTo(SMALL_DATA);
+ }
+
+ @Test
+ @Parameters(method = "getTestParametersForBlocks")
+ public void writeWhileAlreadyCorrupted(Block block) throws Exception {
+ block.service = mPdbService;
+ assertThat(block.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+ assertThat(block.read()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ // In the currently implementation, expect the write to not trigger formatting.
+ assertThat(block.write(ANOTHER_SMALL_DATA)).isEqualTo(ANOTHER_SMALL_DATA.length);
+ }
+
+ @Test
+ public void frpWriteOutOfBound() throws Exception {
+ byte[] maxData = new byte[mPdbService.getMaximumFrpDataSize()];
+ assertThat(mInterface.write(maxData)).isEqualTo(maxData.length);
+
+ byte[] overflowData = new byte[mPdbService.getMaximumFrpDataSize() + 1];
+ assertThat(mInterface.write(overflowData)).isLessThan(0);
+ }
+
+ @Test
+ public void frpCredentialWriteOutOfBound() throws Exception {
+ byte[] maxData = new byte[MAX_FRP_CREDENTIAL_HANDLE_SIZE];
+ mInternalInterface.setFrpCredentialHandle(maxData);
+
+ byte[] overflowData = new byte[MAX_FRP_CREDENTIAL_HANDLE_SIZE + 1];
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setFrpCredentialHandle(overflowData));
+ }
+
+ @Test
+ public void testHardnessWriteOutOfBound() throws Exception {
+ byte[] maxData = new byte[MAX_TEST_MODE_DATA_SIZE];
+ mInternalInterface.setTestHarnessModeData(maxData);
+
+ byte[] overflowData = new byte[MAX_TEST_MODE_DATA_SIZE + 1];
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setTestHarnessModeData(overflowData));
+ }
+
+ @Test
+ public void readCorruptedFrpData() throws Exception {
+ assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+ assertThat(mInterface.read()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ // Expect the read to trigger formatting, resulting in reading empty data.
+ assertThat(mInterface.read()).hasLength(0);
+ }
+
+ @Test
+ public void readCorruptedFrpCredentialData() throws Exception {
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(mInternalInterface.getFrpCredentialHandle()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ assertThrows(IllegalStateException.class, () ->
+ mInternalInterface.getFrpCredentialHandle());
+ }
+
+ @Test
+ public void readCorruptedTestHarnessData() throws Exception {
+ mInternalInterface.setTestHarnessModeData(SMALL_DATA);
+ assertThat(mInternalInterface.getTestHarnessModeData()).isEqualTo(SMALL_DATA);
+
+ tamperWithDigest();
+
+ assertThrows(IllegalStateException.class, () ->
+ mInternalInterface.getTestHarnessModeData());
+ }
+
+ @Test
+ public void nullWrite() throws Exception {
+ assertThrows(NullPointerException.class, () -> mInterface.write(null));
+ mInternalInterface.setFrpCredentialHandle(null); // no exception
+ mInternalInterface.setTestHarnessModeData(null); // no exception
+ }
+
+ @Test
+ public void emptyDataWrite() throws Exception {
+ var empty = new byte[0];
+ assertThat(mInterface.write(empty)).isEqualTo(0);
+
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setFrpCredentialHandle(empty));
+ assertThrows(IllegalArgumentException.class, () ->
+ mInternalInterface.setTestHarnessModeData(empty));
+ }
+
+ @Test
+ public void frpWriteMoreThan100K() throws Exception {
+ File dataBlockFile = mTemporaryFolder.newFile();
+ PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext,
+ dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000);
+ pdbService.setAllowedUid(Binder.getCallingUid());
+ pdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false);
+
+ IPersistentDataBlockService service = pdbService.getInterfaceForTesting();
+ int maxDataSize = (int) service.getMaximumDataBlockSize();
+ assertThat(service.write(new byte[maxDataSize])).isEqualTo(maxDataSize);
+ assertThat(service.write(new byte[maxDataSize + 1])).isEqualTo(-MAX_DATA_BLOCK_SIZE);
+ }
+
+ @Test
+ public void frpBlockReadWriteWithoutPermission() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ assertThrows(SecurityException.class, () -> mInterface.write(SMALL_DATA));
+ assertThrows(SecurityException.class, () -> mInterface.read());
+ }
+
+ @Test
+ public void getMaximumDataBlockSizeDenied() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ assertThrows(SecurityException.class, () -> mInterface.getMaximumDataBlockSize());
+ }
+
+ @Test
+ public void getMaximumDataBlockSize() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+ assertThat(mInterface.getMaximumDataBlockSize())
+ .isEqualTo(mPdbService.getMaximumFrpDataSize());
+ }
+
+ @Test
+ public void getMaximumDataBlockSizeOfLargerPartition() throws Exception {
+ File dataBlockFile = mTemporaryFolder.newFile();
+ PersistentDataBlockService pdbService = new FakePersistentDataBlockService(mContext,
+ dataBlockFile.getPath(), /* blockDeviceSize */ 128 * 1000);
+ pdbService.setAllowedUid(Binder.getCallingUid());
+ pdbService.formatPartitionLocked(/* setOemUnlockEnabled */ false);
+
+ IPersistentDataBlockService service = pdbService.getInterfaceForTesting();
+ assertThat(service.getMaximumDataBlockSize()).isEqualTo(MAX_DATA_BLOCK_SIZE);
+ }
+
+ @Test
+ public void getFrpDataBlockSizeGrantedByUid() throws Exception {
+ assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(SMALL_DATA.length);
+
+ // Modify the magic / type marker. In the current implementation, getting the FRP data block
+ // size does not check digest.
+ tamperWithMagic();
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(0);
+ }
+
+ @Test
+ public void getFrpDataBlockSizeGrantedByPermission() throws Exception {
+ assertThat(mInterface.write(SMALL_DATA)).isEqualTo(SMALL_DATA.length);
+
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ grantAccessPdbStatePermission();
+
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(SMALL_DATA.length);
+
+ // Modify the magic / type marker. In the current implementation, getting the FRP data block
+ // size does not check digest.
+ tamperWithMagic();
+ assertThat(mInterface.getDataBlockSize()).isEqualTo(0);
+ }
+
+ @Test
+ public void wipePermissionCheck() throws Exception {
+ denyOemUnlockPermission();
+ assertThrows(SecurityException.class, () -> mInterface.wipe());
+ }
+
+ @Test
+ public void wipeMakesItNotWritable() throws Exception {
+ grantOemUnlockPermission();
+ mInterface.wipe();
+
+ // Verify that nothing is written.
+ final int headerAndDataBytes = 4 + SMALL_DATA.length;
+ assertThat(mInterface.write(SMALL_DATA)).isLessThan(0);
+ assertThat(readBackingFile(DIGEST_SIZE_BYTES + 4, headerAndDataBytes).array())
+ .isEqualTo(new byte[headerAndDataBytes]);
+
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(readBackingFile(mPdbService.getFrpCredentialDataOffset() + 4,
+ headerAndDataBytes)
+ .array())
+ .isEqualTo(new byte[headerAndDataBytes]);
+
+ mInternalInterface.setTestHarnessModeData(SMALL_DATA);
+ assertThat(readBackingFile(mPdbService.getTestHarnessModeDataOffset() + 4,
+ headerAndDataBytes)
+ .array())
+ .isEqualTo(new byte[headerAndDataBytes]);
+ }
+
+ @Test
+ public void hasFrpCredentialHandleGrantedByUid() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid());
+
+ assertThat(mInterface.hasFrpCredentialHandle()).isFalse();
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(mInterface.hasFrpCredentialHandle()).isTrue();
+ }
+
+ @Test
+ public void hasFrpCredentialHandleGrantedByPermission() throws Exception {
+ mPdbService.setAllowedUid(Binder.getCallingUid() + 1); // unexpected uid
+ grantAccessPdbStatePermission();
+
+ assertThat(mInterface.hasFrpCredentialHandle()).isFalse();
+ mInternalInterface.setFrpCredentialHandle(SMALL_DATA);
+ assertThat(mInterface.hasFrpCredentialHandle()).isTrue();
+ }
+
+ @Test
+ public void clearTestHarnessModeData() throws Exception {
+ mInternalInterface.setTestHarnessModeData(SMALL_DATA);
+ mInternalInterface.clearTestHarnessModeData();
+
+ assertThat(readBackingFile(mPdbService.getTestHarnessModeDataOffset(),
+ MAX_TEST_MODE_DATA_SIZE).array())
+ .isEqualTo(new byte[MAX_TEST_MODE_DATA_SIZE]);
+ }
+
+ @Test
+ public void getAllowedUid() throws Exception {
+ assertThat(mInternalInterface.getAllowedUid()).isEqualTo(Binder.getCallingUid());
+ }
+
+ @Test
+ public void oemUnlockWithoutPermission() throws Exception {
+ denyOemUnlockPermission();
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlockNotAdmin() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(false);
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlock() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+
+ mInterface.setOemUnlockEnabled(true);
+ assertThat(mInterface.getOemUnlockEnabled()).isTrue();
+ assertThat(mOemUnlockPropertyValue).isEqualTo("1");
+ }
+
+ @Test
+ public void oemUnlockUserRestriction_OemUnlock() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_OEM_UNLOCK)))
+ .thenReturn(true);
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlockUserRestriction_FactoryReset() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_FACTORY_RESET)))
+ .thenReturn(true);
+
+ assertThrows(SecurityException.class, () -> mInterface.setOemUnlockEnabled(true));
+ }
+
+ @Test
+ public void oemUnlockIgnoreTampering() throws Exception {
+ grantOemUnlockPermission();
+ makeUserAdmin(true);
+
+ // The current implementation does not check digest before set or get the oem unlock bit.
+ tamperWithDigest();
+ mInterface.setOemUnlockEnabled(true);
+ assertThat(mOemUnlockPropertyValue).isEqualTo("1");
+ tamperWithDigest();
+ assertThat(mInterface.getOemUnlockEnabled()).isTrue();
+ }
+
+ @Test
+ public void getOemUnlockEnabledPermissionCheck_NoPermission() throws Exception {
+ assertThrows(SecurityException.class, () -> mInterface.getOemUnlockEnabled());
+ }
+
+ @Test
+ public void getOemUnlockEnabledPermissionCheck_OemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ assertThat(mInterface.getOemUnlockEnabled()).isFalse();
+ }
+
+ @Test
+ public void getOemUnlockEnabledPermissionCheck_ReadOemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.READ_OEM_UNLOCK_STATE));
+ assertThat(mInterface.getOemUnlockEnabled()).isFalse();
+ }
+
+ @Test
+ public void forceOemUnlock_RequiresNoPermission() throws Exception {
+ denyOemUnlockPermission();
+
+ mInternalInterface.forceOemUnlockEnabled(true);
+
+ assertThat(mOemUnlockPropertyValue).isEqualTo("1");
+ assertThat(readBackingFile(mPdbService.getOemUnlockDataOffset(), 1).array())
+ .isEqualTo(new byte[] { 1 });
+ }
+
+ @Test
+ public void getFlashLockStatePermissionCheck_NoPermission() throws Exception {
+ assertThrows(SecurityException.class, () -> mInterface.getFlashLockState());
+ }
+
+ @Test
+ public void getFlashLockStatePermissionCheck_OemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ mInterface.getFlashLockState(); // Do not throw
+ }
+
+ @Test
+ public void getFlashLockStatePermissionCheck_ReadOemUnlcokState() throws Exception {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.READ_OEM_UNLOCK_STATE));
+ mInterface.getFlashLockState(); // Do not throw
+ }
+
+ private void tamperWithDigest() throws Exception {
+ try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE)) {
+ ch.write(ByteBuffer.wrap("tampered-digest".getBytes()));
+ }
+ }
+
+ private void tamperWithMagic() throws Exception {
+ try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE)) {
+ ch.write(ByteBuffer.wrap("mark".getBytes()), DIGEST_SIZE_BYTES);
+ }
+ }
+
+ private void makeUserAdmin(boolean isAdmin) {
+ when(mUserManager.isUserAdmin(anyInt())).thenReturn(isAdmin);
+ }
+
+ private void grantOemUnlockPermission() {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ doNothing().when(mContext)
+ .enforceCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE),
+ anyString());
+ }
+
+ private void denyOemUnlockPermission() {
+ doReturn(PackageManager.PERMISSION_DENIED).when(mContext)
+ .checkCallingOrSelfPermission(eq(Manifest.permission.OEM_UNLOCK_STATE));
+ }
+
+ private void grantAccessPdbStatePermission() {
+ doReturn(PackageManager.PERMISSION_GRANTED).when(mContext)
+ .checkCallingPermission(eq(Manifest.permission.ACCESS_PDB_STATE));
+ }
+
+ private ByteBuffer readBackingFile(long position, int size) throws Exception {
+ try (var ch = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.READ)) {
+ var buffer = ByteBuffer.allocate(size);
+ assertThat(ch.read(buffer, position)).isGreaterThan(0);
+ return buffer;
+ }
+ }
+}
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f61cce6..daaab33 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -156,6 +156,7 @@
method @Deprecated public int getInt(int);
method @Deprecated public long getLong(int);
method @Deprecated public android.net.Uri getNotificationUri();
+ method @Deprecated public java.util.List<android.net.Uri> getNotificationUris();
method @Deprecated public int getPosition();
method @Deprecated public short getShort(int);
method @Deprecated public String getString(int);
@@ -179,6 +180,7 @@
method @Deprecated public android.os.Bundle respond(android.os.Bundle);
method @Deprecated public void setExtras(android.os.Bundle);
method @Deprecated public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method @Deprecated public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>);
method @Deprecated public void unregisterContentObserver(android.database.ContentObserver);
method @Deprecated public void unregisterDataSetObserver(android.database.DataSetObserver);
}
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
index 9703626..e02492d 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 182940e..fd4ec8b 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -99,6 +99,18 @@
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/invoketest/Android.bp b/tools/hoststubgen/hoststubgen/invoketest/Android.bp
new file mode 100644
index 0000000..7e90e42
--- /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 0000000..34b2145
--- /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 872d568..f32dc72 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.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 @@
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 @@
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 d74612d..aab02b8 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 @@
var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),
+ var annotationAllowedClassesFile: String? = null,
+
var defaultClassLoadHook: String? = null,
var defaultMethodCallHook: String? = null,
@@ -171,6 +173,9 @@
"--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 @@
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 f75062b..937e56c 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 @@
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/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 9f3ec4d..9bb5381e 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.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 @@
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 @@
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 @@
* 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 @@
val cn = classes.getClass(className)
findAnnotation(
+ cn.name,
cn.visibleAnnotations,
cn.invisibleAnnotations,
"class",
@@ -193,6 +205,7 @@
cn.fields?.firstOrNull { it.name == fieldName }?.let {fn ->
findAnnotation(
+ cn.name,
fn.visibleAnnotations,
fn.invisibleAnnotations,
"field",
@@ -229,6 +242,7 @@
// 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/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 46546e8..416f085 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 @@
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 0000000..01a7ab3
--- /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 3dc6da3..e7873d6 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -16,6 +16,7 @@
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 @@
":hoststubgen-test-tiny-framework",
"policy-override-tiny-framework.txt",
],
+ visibility: ["//visibility:private"],
}
java_genrule_host {
@@ -41,6 +43,7 @@
out: [
"host_stub.jar",
],
+ visibility: ["//visibility:private"],
}
java_genrule_host {
@@ -52,6 +55,7 @@
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 @@
":hoststubgen-test-tiny-framework",
"policy-override-tiny-framework.txt",
],
+ visibility: ["//visibility:private"],
}
java_genrule_host {
@@ -82,6 +87,7 @@
out: [
"host_stub.jar",
],
+ visibility: ["//visibility:private"],
}
java_genrule_host {
@@ -93,6 +99,7 @@
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 0000000..bd9e85e
--- /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/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index e212890..872bbf8 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 @@
--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/com/android/hoststubgen/utils/ClassFilterTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
new file mode 100644
index 0000000..f651514
--- /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 ba1d404..4afa2d7 100755
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ b/tools/hoststubgen/scripts/run-all-tests.sh
@@ -33,6 +33,9 @@
# 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.