Merge "Restore Quick affordance interactor test" into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1fdb698..389789b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -209,6 +209,8 @@
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
+ field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE";
+ field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_SOUND_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY";
@@ -4637,7 +4639,7 @@
@FlaggedApi("android.content.pm.verification_service") public final class VerificationSession implements android.os.Parcelable {
method public int describeContents();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long extendTimeRemaining(long);
+ method public long extendTimeRemaining(long);
method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredLibraries();
method @NonNull public android.os.PersistableBundle getExtensionParams();
method public int getId();
@@ -4645,12 +4647,12 @@
method @NonNull public String getPackageName();
method @NonNull public android.content.pm.SigningInfo getSigningInfo();
method @NonNull public android.net.Uri getStagedPackageUri();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+ method public long getTimeoutTime();
method public int getVerificationPolicy();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
+ method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
+ method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
+ method public void reportVerificationIncomplete(int);
+ method public boolean setVerificationPolicy(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e8cec70..aa0e1c2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12833,6 +12833,8 @@
private boolean isImageCaptureIntent() {
return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
|| MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+ || MediaStore.ACTION_MOTION_PHOTO_CAPTURE.equals(mAction)
+ || MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE.equals(mAction)
|| MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
}
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index c911326..ecea479 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -94,9 +94,9 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ @EnforcePermission("VERIFICATION_AGENT")
int getVerificationPolicy();
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ @EnforcePermission("VERIFICATION_AGENT")
boolean setVerificationPolicy(int policy);
}
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 66caf2d..2ab7452 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -24,16 +24,9 @@
* @hide
*/
interface IVerificationSessionInterface {
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long getTimeoutTime(int verificationId);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long extendTimeRemaining(int verificationId, long additionalMs);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
boolean setVerificationPolicy(int verificationId, int policy);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
void reportVerificationIncomplete(int verificationId, int reason);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
- void reportVerificationComplete(int verificationId, in VerificationStatus status);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
- void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response);
+ void reportVerificationComplete(int verificationId, in VerificationStatus status, in @nullable PersistableBundle extensionResponse);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 4ade211..97f78e0 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -19,7 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
@@ -166,8 +165,8 @@
/**
* Get the value of Clock.elapsedRealtime() at which time this verification
* will timeout as incomplete if no other verification response is provided.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public long getTimeoutTime() {
try {
return mSession.getTimeoutTime(mId);
@@ -190,8 +189,8 @@
/**
* Override the verification policy for this session.
* @return True if the override was successful, False otherwise.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
if (mVerificationPolicy == policy) {
// No effective policy change
@@ -215,8 +214,8 @@
* This may be called multiple times. If the request would bypass any max
* duration by the system, the method will return a lower value than the
* requested amount that indicates how much the time was extended.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public long extendTimeRemaining(long additionalMs) {
try {
return mSession.extendTimeRemaining(mId, additionalMs);
@@ -227,9 +226,9 @@
/**
* Report to the system that verification could not be completed along
- * with an approximate reason to pass on to the installer.
+ * with an approximate reason to pass on to the installer.]
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) {
try {
mSession.reportVerificationIncomplete(mId, reason);
@@ -242,11 +241,11 @@
* Report to the system that the verification has completed and the
* install process may act on that status to either block in the case
* of failure or continue to process the install in the case of success.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationComplete(@NonNull VerificationStatus status) {
try {
- mSession.reportVerificationComplete(mId, status);
+ mSession.reportVerificationComplete(mId, status, /* extensionResponse= */ null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -256,12 +255,12 @@
* Same as {@link #reportVerificationComplete(VerificationStatus)}, but also provide
* a result to the extension params provided in the request, which will be passed to the
* installer in the installation result.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationComplete(@NonNull VerificationStatus status,
- @NonNull PersistableBundle response) {
+ @NonNull PersistableBundle extensionResponse) {
try {
- mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response);
+ mSession.reportVerificationComplete(mId, status, extensionResponse);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index e68c4ca..6325b00 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -370,7 +370,10 @@
* and most battery stats resets.
*/
public Builder accumulated() {
- mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED;
+ mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_SCREEN_STATE
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA;
return this;
}
diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java
index 9cbc5ec..90906ed 100644
--- a/core/java/android/security/forensic/ForensicEvent.java
+++ b/core/java/android/security/forensic/ForensicEvent.java
@@ -61,6 +61,14 @@
in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class);
}
+ public String getType() {
+ return mType;
+ }
+
+ public Map<String, String> getKeyValuePairs() {
+ return mKeyValuePairs;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mType);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e6de478..94f415b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -412,41 +412,28 @@
*/
public static class JankData {
- /** @hide */
- @IntDef(flag = true, value = {JANK_NONE,
- DISPLAY_HAL,
- JANK_SURFACEFLINGER_DEADLINE_MISSED,
- JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED,
- JANK_APP_DEADLINE_MISSED,
- PREDICTION_ERROR,
- SURFACE_FLINGER_SCHEDULING})
+ /**
+ * Needs to be kept in sync with android_view_SurfaceControl.cpp's
+ * JankDataListenerWrapper::onJankDataAvailable.
+ * @hide
+ */
+ @IntDef(flag = true, value = {
+ JANK_NONE,
+ JANK_COMPOSER,
+ JANK_APPLICATION,
+ JANK_OTHER,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface JankType {}
- // Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h
-
// No Jank
- public static final int JANK_NONE = 0x0;
-
- // Jank not related to SurfaceFlinger or the App
- public static final int DISPLAY_HAL = 0x1;
- // SF took too long on the CPU
- public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2;
- // SF took too long on the GPU
- public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
- // Either App or GPU took too long on the frame
- public static final int JANK_APP_DEADLINE_MISSED = 0x8;
- // Vsync predictions have drifted beyond the threshold from the actual HWVsync
- public static final int PREDICTION_ERROR = 0x10;
- // Latching a buffer early might cause an early present of the frame
- public static final int SURFACE_FLINGER_SCHEDULING = 0x20;
- // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
- // presented later because the previous buffer was presented in its expected vsync. This
- // usually happens if there is an unexpectedly long frame causing the rest of the buffers
- // to enter a stuffed state.
- public static final int BUFFER_STUFFING = 0x40;
- // Jank due to unknown reasons.
- public static final int UNKNOWN = 0x80;
+ public static final int JANK_NONE = 0;
+ // Jank caused by the composer missing a deadline
+ public static final int JANK_COMPOSER = 1 << 0;
+ // Jank caused by the application missing the composer's deadline
+ public static final int JANK_APPLICATION = 1 << 1;
+ // Jank due to other unknown reasons
+ public static final int JANK_OTHER = 1 << 2;
public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs,
long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) {
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 003393c..0af4bea 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -16,13 +16,9 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.DISPLAY_HAL;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
-import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE;
import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE;
@@ -181,23 +177,11 @@
case JANK_NONE:
str.append("JANK_NONE");
break;
- case JANK_APP_DEADLINE_MISSED:
- str.append("JANK_APP_DEADLINE_MISSED");
+ case JANK_APPLICATION:
+ str.append("JANK_APPLICATION");
break;
- case JANK_SURFACEFLINGER_DEADLINE_MISSED:
- str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED");
- break;
- case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED:
- str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED");
- break;
- case DISPLAY_HAL:
- str.append("DISPLAY_HAL");
- break;
- case PREDICTION_ERROR:
- str.append("PREDICTION_ERROR");
- break;
- case SURFACE_FLINGER_SCHEDULING:
- str.append("SURFACE_FLINGER_SCHEDULING");
+ case JANK_COMPOSER:
+ str.append("JANK_COMPOSER");
break;
default:
str.append("UNKNOWN: ").append(jankType);
@@ -628,16 +612,12 @@
if (info.surfaceControlCallbackFired) {
totalFramesCount++;
boolean missedFrame = false;
- if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
+ if ((info.jankType & JANK_APPLICATION) != 0) {
Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name);
missedAppFramesCount++;
missedFrame = true;
}
- if ((info.jankType & DISPLAY_HAL) != 0
- || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
- || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
- || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
- || (info.jankType & PREDICTION_ERROR) != 0) {
+ if ((info.jankType & JANK_COMPOSER) != 0) {
Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name);
missedSfFramesCount++;
missedFrame = true;
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index 07df248..25a9fbc 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -2,6 +2,48 @@
container: "system"
flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_1"
+ description: "Ravenwood test RW flag 1"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_2"
+ description: "Ravenwood test RW flag 2"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_1"
+ description: "Ravenwood test RO flag 1"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_2"
+ description: "Ravenwood test RO flag 2"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_apache_http_legacy_preload"
namespace: "system_performance"
description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library."
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 10e49ef..50252c1 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -27,6 +27,7 @@
#include <camera/StringUtils.h>
#include <com_android_internal_camera_flags.h>
#include <cutils/properties.h>
+#include <gui/Flags.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <nativehelper/JNIHelp.h>
@@ -715,16 +716,20 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
- sp<IGraphicBufferProducer> gbp;
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
- if (surface != NULL) {
- gbp = surface->getIGraphicBufferProducer();
- }
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
+ sp<IGraphicBufferProducer> gbp;
+ if (surface != NULL) {
+ gbp = surface->getIGraphicBufferProducer();
+ }
if (camera->setPreviewTarget(gbp) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException", "setPreviewTexture failed");
}
}
@@ -736,6 +741,9 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ sp<Surface> surface;
+#endif
sp<IGraphicBufferProducer> producer = NULL;
if (jSurfaceTexture != NULL) {
producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
@@ -744,10 +752,16 @@
"SurfaceTexture already released in setPreviewTexture");
return;
}
-
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ surface = new Surface(producer);
+#endif
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
if (camera->setPreviewTarget(producer) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException",
"setPreviewTexture failed");
}
@@ -761,18 +775,32 @@
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
sp<IGraphicBufferProducer> gbp;
+#endif
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
+ if (surface == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "android_view_Surface_getSurface failed");
+ return;
+ }
+
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
if (surface != NULL) {
gbp = surface->getIGraphicBufferProducer();
}
+#endif
}
// Clear out normal preview callbacks
context->setCallbackMode(env, false, false);
// Then set up callback surface
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewCallbackTarget(surface) != NO_ERROR) {
+#else
if (camera->setPreviewCallbackTarget(gbp) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException", "setPreviewCallbackTarget failed");
}
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 755704a..f162b74 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -35,6 +35,7 @@
#include <android_runtime/android_view_SurfaceSession.h>
#include <cutils/ashmem.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/JankInfo.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
@@ -2161,9 +2162,30 @@
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (size_t i = 0; i < jankData.size(); i++) {
+ // The exposed constants in SurfaceControl are simplified, so we need to translate the
+ // jank type we get from SF to what is exposed in Java.
+ int sfJankType = jankData[i].jankType;
+ int javaJankType = 0x0; // SurfaceControl.JankData.JANK_NONE
+ if (sfJankType &
+ (JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::PredictionError |
+ JankType::SurfaceFlingerScheduling)) {
+ javaJankType |= 0x1; // SurfaceControl.JankData.JANK_COMPOSER
+ }
+ if (sfJankType & JankType::AppDeadlineMissed) {
+ javaJankType |= 0x2; // SurfaceControl.JankData.JANK_APPLICATION
+ }
+ if (sfJankType &
+ ~(JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+ JankType::PredictionError | JankType::SurfaceFlingerScheduling |
+ JankType::BufferStuffing | JankType::SurfaceFlingerStuffing)) {
+ javaJankType |= 0x4; // SurfaceControl.JankData.JANK_OTHER
+ }
+
jobject jJankData =
env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
- jankData[i].frameVsyncId, jankData[i].jankType,
+ jankData[i].frameVsyncId, javaJankType,
jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
jankData[i].actualAppFrameTimeNs);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fb06e96..4c38246 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8483,6 +8483,27 @@
<permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
android:protectionLevel="signature"/>
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its picture profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its sound profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
<!-- @SystemApi
@FlaggedApi("android.content.pm.verification_service")
Allows app to be the verification agent to verify packages.
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 90ae306..f1e1df5 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -142,10 +142,10 @@
new VerificationStatus.Builder().setVerified(true).build();
mTestSession.reportVerificationComplete(status);
verify(mTestSessionInterface, times(1)).reportVerificationComplete(
- eq(TEST_ID), eq(status));
+ eq(TEST_ID), eq(status), eq(null));
mTestSession.reportVerificationComplete(status, response);
verify(mTestSessionInterface, times(1))
- .reportVerificationCompleteWithExtensionResponse(
+ .reportVerificationComplete(
eq(TEST_ID), eq(status), eq(response));
final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 1cbc7d6..60b5a42 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -16,9 +16,9 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
@@ -164,7 +164,7 @@
verify(mRenderer, only()).addObserver(any());
// send first frame with a long duration - should not be taken into account
- sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFirstWindowFrame(tracker, 100, JANK_APPLICATION, 100L);
// send another frame with a short duration - should not be considered janky
sendFrame(tracker, 5, JANK_NONE, 101L);
@@ -173,7 +173,7 @@
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(tracker, 5, JANK_NONE, 102L);
- sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, 500, JANK_APPLICATION, 103L);
verify(tracker).removeObservers();
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -202,7 +202,7 @@
sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_COMPOSER, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -236,7 +236,7 @@
verify(mRenderer, only()).addObserver(any());
// send first frame - janky
- sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, 40, JANK_APPLICATION, 100L);
// send another frame - not jank
sendFrame(tracker, 4, JANK_NONE, 101L);
@@ -275,7 +275,7 @@
sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_APPLICATION, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -317,7 +317,7 @@
// end the trace session, simulate one more valid callback came after the end call.
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 102L);
// One more callback with VSYNC after the end() vsync id.
sendFrame(tracker, 4, JANK_NONE, 103L);
@@ -365,7 +365,7 @@
sendSfFrame(tracker, 4, 102L, JANK_NONE);
// Send janky but complete callbck fo 103L
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 103L);
verify(tracker).removeObservers();
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -397,7 +397,7 @@
sendFrame(tracker, 4, JANK_NONE, 101L);
// a janky frame
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 102L);
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(tracker).removeObservers();
@@ -481,7 +481,7 @@
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// a janky frame
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, JANK_APPLICATION, 102L);
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
@@ -514,7 +514,7 @@
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - janky
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, JANK_APPLICATION, 100L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// normal frame - not janky
@@ -561,7 +561,7 @@
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
// janky frame, should be ignored, trigger finish
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, JANK_APPLICATION, 103L);
verify(mJankStatsRegistration).removeAfter(anyLong());
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -623,16 +623,16 @@
tracker.begin();
mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, JANK_COMPOSER, 100L);
+ sendFrame(tracker, JANK_COMPOSER, 101L);
+ sendFrame(tracker, JANK_APPLICATION, 102L);
sendFrame(tracker, JANK_NONE, 103L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 104L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 105L);
+ sendFrame(tracker, JANK_APPLICATION, 104L);
+ sendFrame(tracker, JANK_APPLICATION, 105L);
when(mChoreographer.getVsyncId()).thenReturn(106L);
tracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L);
+ sendFrame(tracker, JANK_COMPOSER, 106L);
+ sendFrame(tracker, JANK_COMPOSER, 107L);
verify(mJankStatsRegistration).removeAfter(anyLong());
verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 9fd255d..7adec39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -20,6 +20,7 @@
import android.graphics.Point
import android.graphics.Rect
import android.util.Log
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -102,14 +103,17 @@
hideEducation(animated = false)
log { "showStackEducation at: $position" }
+ val rootBounds = Rect()
+ // Get root bounds on screen as position is in screen coordinates
+ root.getBoundsOnScreen(rootBounds)
educationView =
createEducationView(R.layout.bubble_bar_stack_education, root).apply {
setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
- setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
- updateEducationPosition(view = this, position, root)
+ updateEducationPosition(view = this, position, rootBounds)
val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
doOnLayout {
- it.pivotX = it.width - arrowToEdgeOffset
+ it.pivotX = if (position.x < rootBounds.centerX())
+ arrowToEdgeOffset else it.width - arrowToEdgeOffset
it.pivotY = it.height.toFloat()
}
setOnClickListener { educationClickHandler() }
@@ -218,12 +222,9 @@
*
* @param view the user education view to layout
* @param position the reference position in Screen coordinates
- * @param root the root view to use for the layout
+ * @param rootBounds bounds of the parent the education view is placed in
*/
- private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) {
- val rootBounds = Rect()
- // Get root bounds on screen as position is in screen coordinates
- root.getBoundsOnScreen(rootBounds)
+ private fun updateEducationPosition(view: BubblePopupView, position: Point, rootBounds: Rect) {
// Get the offset to the arrow from the edge of the education view
val arrowToEdgeOffset =
view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt()
@@ -231,7 +232,15 @@
// Calculate education view margins
val params = view.layoutParams as FrameLayout.LayoutParams
params.bottomMargin = rootBounds.bottom - position.y
- params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ if (position.x < rootBounds.centerX()) {
+ params.leftMargin = position.x - arrowToEdgeOffset
+ params.gravity = Gravity.LEFT or Gravity.BOTTOM
+ view.setArrowPosition(BubblePopupDrawable.ArrowPosition.Start)
+ } else {
+ params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ params.gravity = Gravity.RIGHT or Gravity.BOTTOM
+ view.setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
+ }
view.layoutParams = params
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2278789..4e181e3 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -421,9 +421,9 @@
}
flag {
- name: "status_bar_notification_chips"
+ name: "status_bar_ron_chips"
namespace: "systemui"
- description: "Show promoted ongoing notifications as chips in the status bar"
+ description: "Show rich ongoing notifications as chips in the status bar"
bug: "361346412"
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index eb5d931..2872900 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -19,11 +19,12 @@
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ron.ui.viewmodel.notifChipsViewModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
@@ -41,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
class NotifChipsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
similarity index 74%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
index 69a7627..118dea6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
import android.content.packageManager
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -40,13 +40,13 @@
import org.mockito.kotlin.whenever
@SmallTest
-class DemoNotifChipViewModelTest : SysuiTestCase() {
+class DemoRonChipViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val commandRegistry = kosmos.commandRegistry
private val pw = PrintWriter(StringWriter())
- private val underTest = kosmos.demoNotifChipViewModel
+ private val underTest = kosmos.demoRonChipViewModel
@Before
fun setUp() {
@@ -56,61 +56,61 @@
}
@Test
- @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_flagOff_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- addDemoNotifChip()
+ addDemoRonChip()
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@Test
- @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_noPackage_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- commandRegistry.onShellCommand(pw, arrayOf("demo-notif"))
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron"))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@Test
- @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_hasPackage_shown() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
}
@Test
- @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_hasText_shownWithText() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
commandRegistry.onShellCommand(
pw,
- arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"),
+ arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test")
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
}
@Test
- @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_supportsColor() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
commandRegistry.onShellCommand(
pw,
- arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"),
+ arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343")
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
@@ -119,28 +119,28 @@
}
@Test
- @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_hasHideArg_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
// First, show a chip
- addDemoNotifChip()
+ addDemoRonChip()
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
// Then, hide the chip
- commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide"))
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide"))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
- private fun addDemoNotifChip() {
- addDemoNotifChip(commandRegistry, pw)
+ private fun addDemoRonChip() {
+ Companion.addDemoRonChip(commandRegistry, pw)
}
companion object {
- fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
- commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
+ fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index e96def6..26ce7b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -25,6 +25,7 @@
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -39,8 +40,7 @@
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -66,11 +66,13 @@
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */
+/**
+ * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled.
+ */
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(StatusBarNotifChips.FLAG_NAME)
+@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
@@ -97,11 +99,11 @@
@Before
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
- kosmos.demoNotifChipViewModel.start()
+ kosmos.demoRonChipViewModel.start()
val icon =
BitmapDrawable(
context.resources,
- Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
+ Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
)
whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
}
@@ -323,7 +325,7 @@
latest: OngoingActivityChipModel?,
chipView: View,
dialog: SystemUIDialog,
- kosmos: Kosmos,
+ kosmos: Kosmos
): DialogInterface.OnClickListener {
// Capture the action that would get invoked when the user clicks "Stop" on the dialog
lateinit var dialogStopAction: DialogInterface.OnClickListener
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
similarity index 93%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
index b12d7c5..c5b857f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
@@ -24,6 +24,7 @@
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -36,10 +37,9 @@
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -73,12 +73,14 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */
+/**
+ * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled.
+ */
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(StatusBarNotifChips.FLAG_NAME)
-class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
+@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val systemClock = kosmos.fakeSystemClock
@@ -108,7 +110,7 @@
@Before
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
- kosmos.demoNotifChipViewModel.start()
+ kosmos.demoRonChipViewModel.start()
val icon =
BitmapDrawable(
context.resources,
@@ -117,7 +119,7 @@
whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
}
- // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the
+ // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the
// flow has the right behavior to verify that we don't break any existing functionality.
@Test
@@ -254,13 +256,13 @@
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- addDemoNotifChip(commandRegistry, pw)
+ addDemoRonChip(commandRegistry, pw)
val latest by collectLastValue(underTest.chips)
assertIsScreenRecordChip(latest!!.primary)
assertIsCallChip(latest!!.secondary)
- // Demo notif chip is dropped
+ // Demo RON chip is dropped
}
@Test
@@ -387,7 +389,7 @@
fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
testScope.runTest {
// Start with just the lowest priority chip shown
- addDemoNotifChip(commandRegistry, pw)
+ addDemoRonChip(commandRegistry, pw)
// And everything else hidden
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -395,7 +397,7 @@
val latest by collectLastValue(underTest.primaryChip)
- assertIsDemoNotifChip(latest)
+ assertIsDemoRonChip(latest)
// WHEN the higher priority call chip is added
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
@@ -429,7 +431,7 @@
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- addDemoNotifChip(commandRegistry, pw)
+ addDemoRonChip(commandRegistry, pw)
val latest by collectLastValue(underTest.primaryChip)
@@ -451,15 +453,15 @@
// WHEN the higher priority call is removed
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- // THEN the lower priority demo notif is used
- assertIsDemoNotifChip(latest)
+ // THEN the lower priority demo RON is used
+ assertIsDemoRonChip(latest)
}
@Test
fun chips_movesChipsAroundAccordingToPriority() =
testScope.runTest {
// Start with just the lowest priority chip shown
- addDemoNotifChip(commandRegistry, pw)
+ addDemoRonChip(commandRegistry, pw)
// And everything else hidden
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -467,16 +469,16 @@
val latest by collectLastValue(underTest.chips)
- assertIsDemoNotifChip(latest!!.primary)
+ assertIsDemoRonChip(latest!!.primary)
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
// WHEN the higher priority call chip is added
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- // THEN the higher priority call chip is used as primary and demo notif is demoted to
+ // THEN the higher priority call chip is used as primary and demo ron is demoted to
// secondary
assertIsCallChip(latest!!.primary)
- assertIsDemoNotifChip(latest!!.secondary)
+ assertIsDemoRonChip(latest!!.secondary)
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -487,7 +489,7 @@
)
// THEN the higher priority media projection chip is used as primary and call is demoted
- // to secondary (and demo notif is dropped altogether)
+ // to secondary (and demo RON is dropped altogether)
assertIsShareToAppChip(latest!!.primary)
assertIsCallChip(latest!!.secondary)
@@ -501,15 +503,15 @@
screenRecordState.value = ScreenRecordModel.DoingNothing
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- // THEN media projection and demo notif remain
+ // THEN media projection and demo RON remain
assertIsShareToAppChip(latest!!.primary)
- assertIsDemoNotifChip(latest!!.secondary)
+ assertIsDemoRonChip(latest!!.secondary)
// WHEN media projection is dropped
mediaProjectionState.value = MediaProjectionState.NotProjecting
- // THEN demo notif is promoted to primary
- assertIsDemoNotifChip(latest!!.primary)
+ // THEN demo RON is promoted to primary
+ assertIsDemoRonChip(latest!!.primary)
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@@ -622,7 +624,7 @@
assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
}
- private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) {
+ private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
.isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
new file mode 100644
index 0000000..bfafdab
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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.volume.dialog.sliders.domain.interactor
+
+import android.testing.TestableLooper
+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.kosmos.testScope
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSliderInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: VolumeDialogSliderInteractor
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.volumeDialogSliderInteractor
+ }
+
+ @Test
+ fun settingStreamVolume_setsActiveStream() =
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ // initialize the stream model
+ fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
+
+ val sliderModel by collectLastValue(underTest.slider)
+ underTest.setStreamVolume(1)
+ runCurrent()
+
+ assertThat(sliderModel!!.isActive).isTrue()
+ }
+ }
+
+ @Test
+ fun streamVolumeIs_minMaxAreEnforced() =
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ fakeVolumeDialogController.updateState {
+ states.put(
+ volumeDialogSliderType.audioStream,
+ VolumeDialogController.StreamState().apply {
+ levelMin = 0
+ level = 2
+ levelMax = 1
+ },
+ )
+ }
+
+ val sliderModel by collectLastValue(underTest.slider)
+ runCurrent()
+
+ assertThat(sliderModel!!.level).isEqualTo(1)
+ }
+ }
+}
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index 7d7498f..98e7505 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -18,5 +18,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/volume_dialog_background_corner_radius" />
- <solid android:color="?androidprv:attr/materialColorSurface" />
+ <solid android:color="?androidprv:attr/colorSurface" />
</shape>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
deleted file mode 100644
index f77db95..0000000
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
-
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
-
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
-
- <Button
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
deleted file mode 100644
index f77db95..0000000
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- Copyright (C) 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
-
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
-
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
-
- <Button
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f77db95..3013e90 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -28,7 +28,6 @@
android:id="@+id/volume_dialog_floating_sliders_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
android:divider="@drawable/volume_dialog_floating_sliders_spacer"
android:gravity="bottom"
android:orientation="horizontal"
@@ -36,6 +35,7 @@
android:showDividers="middle" />
<LinearLayout
+ android:id="@+id/volume_dialog"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="wrap_content"
android:background="@drawable/volume_dialog_background"
@@ -50,11 +50,9 @@
android:layout_width="@dimen/volume_dialog_button_size"
android:layout_height="@dimen/volume_dialog_button_size" />
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
+ <include layout="@layout/volume_dialog_slider" />
- <Button
+ <ImageButton
android:id="@+id/volume_dialog_settings"
android:layout_width="@dimen/volume_dialog_button_size"
android:layout_height="@dimen/volume_dialog_button_size"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
index 8ce0dbf..be733d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -20,7 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -31,8 +31,8 @@
abstract class StatusBarChipsModule {
@Binds
@IntoMap
- @ClassKey(DemoNotifChipViewModel::class)
- abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable
+ @ClassKey(DemoRonChipViewModel::class)
+ abstract fun binds(impl: DemoRonChipViewModel): CoreStartable
companion object {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
index 5fa19dd..cce9a16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
@@ -22,7 +22,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
@@ -37,25 +37,25 @@
import kotlinx.coroutines.flow.asStateFlow
/**
- * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb
- * commands sent by the user.
+ * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on
+ * adb commands sent by the user.
*
* Example adb commands:
*
* To show a chip with the SysUI icon and custom text and color:
* ```
- * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343"
+ * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343"
* ```
*
* To hide the chip:
* ```
- * adb shell cmd statusbar demo-notif --hide
+ * adb shell cmd statusbar demo-ron --hide
* ```
*
- * See [DemoNotifCommand] for more information on the adb command spec.
+ * See [DemoRonCommand] for more information on the adb command spec.
*/
@SysUISingleton
-class DemoNotifChipViewModel
+class DemoRonChipViewModel
@Inject
constructor(
private val commandRegistry: CommandRegistry,
@@ -63,19 +63,19 @@
private val systemClock: SystemClock,
) : OngoingActivityChipViewModel, CoreStartable {
override fun start() {
- commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() }
+ commandRegistry.registerCommand("demo-ron") { DemoRonCommand() }
}
private val _chip =
MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
- private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) {
+ private inner class DemoRonCommand : ParseableCommand("demo-ron") {
private val packageName: String? by
param(
longName = "packageName",
shortName = "p",
- description = "The package name for app \"posting\" the demo notification",
+ description = "The package name for the demo RON app",
valueParser = Type.String,
)
@@ -99,12 +99,15 @@
)
private val hide by
- flag(longName = "hide", description = "Hides any existing demo notification chip")
+ flag(
+ longName = "hide",
+ description = "Hides any existing demo RON chip",
+ )
override fun execute(pw: PrintWriter) {
- if (!StatusBarNotifChips.isEnabled) {
+ if (!StatusBarRonChips.isEnabled) {
pw.println(
- "Error: com.android.systemui.status_bar_notification_chips must be enabled " +
+ "Error: com.android.systemui.status_bar_ron_chips must be enabled " +
"before using this demo feature"
)
return
@@ -164,12 +167,8 @@
return null
}
return OngoingActivityChipModel.ChipIcon.FullColorAppIcon(
- Icon.Loaded(drawable = iconDrawable, contentDescription = null)
+ Icon.Loaded(drawable = iconDrawable, contentDescription = null),
)
}
}
-
- companion object {
- private const val DEMO_COMMAND_NAME = "demo-notif"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
index 47ffbaf..4ef1909 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.shared
+package com.android.systemui.statusbar.chips.ron.shared
import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
-/** Helper for reading or using the status bar promoted notification chips flag state. */
+/** Helper for reading or using the status bar RON chips flag state. */
@Suppress("NOTHING_TO_INLINE")
-object StatusBarNotifChips {
+object StatusBarRonChips {
/** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
/** A token used for dependency declaration */
val token: FlagToken
@@ -33,7 +33,7 @@
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.statusBarNotificationChips()
+ get() = Flags.statusBarRonChips()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index f4462a4..2220caab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -28,7 +28,7 @@
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
@@ -102,7 +102,7 @@
defaultIconView.tintView(iconTint)
}
is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
- StatusBarNotifChips.assertInNewMode()
+ StatusBarRonChips.assertInNewMode()
IconViewBinder.bind(icon.impl, defaultIconView)
defaultIconView.visibility = View.VISIBLE
defaultIconView.untintView()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index cf07af1..2366572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -20,7 +20,7 @@
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
/** Model representing the display of an ongoing activity as a chip in the status bar. */
sealed class OngoingActivityChipModel {
@@ -91,7 +91,10 @@
override val onClickListener: View.OnClickListener?,
) : Shown(icon, colors, onClickListener) {
init {
- StatusBarNotifChips.assertInNewMode()
+ check(StatusBarRonChips.isEnabled) {
+ "OngoingActivityChipModel.Shown.ShortTimeDelta created even though " +
+ "Flags.statusBarRonChips is not enabled"
+ }
}
override val logName = "Shown.ShortTimeDelta"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index ed32597..954386e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -23,9 +24,9 @@
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
@@ -56,7 +57,7 @@
castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
callChipViewModel: CallChipViewModel,
notifChipsViewModel: NotifChipsViewModel,
- demoNotifChipViewModel: DemoNotifChipViewModel,
+ demoRonChipViewModel: DemoRonChipViewModel,
@StatusBarChipsLog private val logger: LogBuffer,
) {
private enum class ChipType {
@@ -65,8 +66,8 @@
CastToOtherDevice,
Call,
Notification,
- /** A demo of a notification chip, used just for testing. */
- DemoNotification,
+ /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */
+ DemoRon,
}
/** Model that helps us internally track the various chip states from each of the types. */
@@ -88,7 +89,7 @@
val castToOtherDevice: OngoingActivityChipModel.Hidden,
val call: OngoingActivityChipModel.Hidden,
val notifs: OngoingActivityChipModel.Hidden,
- val demoNotif: OngoingActivityChipModel.Hidden,
+ val demoRon: OngoingActivityChipModel.Hidden,
) : InternalChipModel
}
@@ -98,7 +99,7 @@
val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
val notifs: List<OngoingActivityChipModel.Shown> = emptyList(),
- val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
)
/** Bundles all the incoming chips into one object to easily pass to various flows. */
@@ -109,8 +110,8 @@
castToOtherDeviceChipViewModel.chip,
callChipViewModel.chip,
notifChipsViewModel.chips,
- demoNotifChipViewModel.chip,
- ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif ->
+ demoRonChipViewModel.chip,
+ ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoRon ->
logger.log(
TAG,
LogLevel.INFO,
@@ -128,9 +129,9 @@
str1 = call.logName
// TODO(b/364653005): Log other information for notification chips.
str2 = notifs.map { it.logName }.toString()
- str3 = demoNotif.logName
+ str3 = demoRon.logName
},
- { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" },
+ { "... > Call=$str1 > Notifs=$str2 > DemoRon=$str3" },
)
ChipBundle(
screenRecord = screenRecord,
@@ -138,7 +139,7 @@
castToOtherDevice = castToOtherDevice,
call = call,
notifs = notifs,
- demoNotif = demoNotif,
+ demoRon = demoRon,
)
}
// Some of the chips could have timers in them and we don't want the start time
@@ -197,9 +198,9 @@
* actually displaying the chip.
*/
val chips: StateFlow<MultipleOngoingActivityChipsModel> =
- if (!StatusBarNotifChips.isEnabled) {
- // Multiple chips are only allowed with notification chips. If the flag isn't on, use
- // just the primary chip.
+ if (!Flags.statusBarRonChips()) {
+ // Multiple chips are only allowed with RONs. If the flag isn't on, use just the
+ // primary chip.
primaryChip
.map {
MultipleOngoingActivityChipsModel(
@@ -281,12 +282,11 @@
remainingChips =
bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)),
)
- bundle.demoNotif is OngoingActivityChipModel.Shown -> {
- StatusBarNotifChips.assertInNewMode()
+ bundle.demoRon is OngoingActivityChipModel.Shown -> {
+ StatusBarRonChips.assertInNewMode()
MostImportantChipResult(
- mostImportantChip =
- InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif),
- remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()),
+ mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon),
+ remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()),
)
}
else -> {
@@ -296,7 +296,7 @@
check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden)
check(bundle.call is OngoingActivityChipModel.Hidden)
check(bundle.notifs.isEmpty())
- check(bundle.demoNotif is OngoingActivityChipModel.Hidden)
+ check(bundle.demoRon is OngoingActivityChipModel.Hidden)
MostImportantChipResult(
mostImportantChip =
InternalChipModel.Hidden(
@@ -305,7 +305,7 @@
castToOtherDevice = bundle.castToOtherDevice,
call = bundle.call,
notifs = OngoingActivityChipModel.Hidden(),
- demoNotif = bundle.demoNotif,
+ demoRon = bundle.demoRon,
),
// All the chips are already hidden, so no need to filter anything out of the
// bundle.
@@ -334,7 +334,7 @@
ChipType.CastToOtherDevice -> new.castToOtherDevice
ChipType.Call -> new.call
ChipType.Notification -> new.notifs
- ChipType.DemoNotification -> new.demoNotif
+ ChipType.DemoRon -> new.demoRon
}
} else if (new is InternalChipModel.Shown) {
// If we have a chip to show, always show it.
@@ -356,7 +356,7 @@
castToOtherDevice = OngoingActivityChipModel.Hidden(),
call = OngoingActivityChipModel.Hidden(),
notifs = OngoingActivityChipModel.Hidden(),
- demoNotif = OngoingActivityChipModel.Hidden(),
+ demoRon = OngoingActivityChipModel.Hidden(),
)
private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 697a6ce..5900fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -17,7 +17,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
@@ -78,7 +78,7 @@
/** The notifications that are promoted and ongoing. Sorted by priority order. */
val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> =
- if (StatusBarNotifChips.isEnabled) {
+ if (StatusBarRonChips.isEnabled) {
// TODO(b/364653005): Filter all the notifications down to just the promoted ones.
// TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
// instead of being separate.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 013141b..f85785f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -56,7 +56,7 @@
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips;
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
@@ -657,7 +657,7 @@
}
boolean showSecondaryOngoingActivityChip =
Flags.statusBarScreenSharingChips()
- && StatusBarNotifChips.isEnabled()
+ && StatusBarRonChips.isEnabled()
&& mHasSecondaryOngoingActivity;
return new StatusBarVisibilityModel(
@@ -699,7 +699,7 @@
boolean showSecondaryOngoingActivityChip =
// Secondary chips are only supported when RONs are enabled.
- StatusBarNotifChips.isEnabled()
+ StatusBarRonChips.isEnabled()
&& visibilityModel.getShowSecondaryOngoingActivityChip()
&& !disableNotifications;
if (showSecondaryOngoingActivityChip) {
@@ -832,7 +832,7 @@
}
private void showSecondaryOngoingActivityChip(boolean animate) {
- StatusBarNotifChips.assertInNewMode();
+ StatusBarRonChips.assertInNewMode();
StatusBarSimpleFragment.assertInLegacyMode();
animateShow(mSecondaryOngoingActivityChip, animate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 11d7339..473f956 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -27,7 +27,6 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
@@ -85,7 +84,7 @@
}
}
- if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) {
+ if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) {
val primaryChipView: View =
view.requireViewById(R.id.ongoing_activity_chip_primary)
launch {
@@ -121,7 +120,7 @@
}
}
- if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) {
+ if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) {
val primaryChipView: View =
view.requireViewById(R.id.ongoing_activity_chip_primary)
val secondaryChipView: View =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 3d125b8..fa108842 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -105,8 +105,8 @@
scope.trySend(VolumeDialogEventModel.ShowSafetyWarning(flags))
}
- override fun onAccessibilityModeChanged(showA11yStream: Boolean) {
- scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream))
+ override fun onAccessibilityModeChanged(showA11yStream: Boolean?) {
+ scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true))
}
// Captions button is remove from the Volume Dialog
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index 2668589b..fb108c5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -60,7 +60,7 @@
) {
@SuppressLint("SharedFlowCreation")
- private val mutableDismissDialogEvents = MutableSharedFlow<Unit>()
+ private val mutableDismissDialogEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
init {
@@ -74,7 +74,7 @@
.mapNotNull { it.toVisibilityModel() }
.onEach { model ->
updateVisibility { model }
- if (model is VolumeDialogVisibilityModel.Visible) {
+ if (model is Visible) {
resetDismissTimeout()
}
}
@@ -87,17 +87,17 @@
*/
fun dismissDialog(reason: Int) {
updateVisibility { visibilityModel ->
- if (visibilityModel is VolumeDialogVisibilityModel.Dismissed) {
+ if (visibilityModel is Dismissed) {
visibilityModel
} else {
- VolumeDialogVisibilityModel.Dismissed(reason)
+ Dismissed(reason)
}
}
}
/** Resets current dialog timeout. */
- suspend fun resetDismissTimeout() {
- mutableDismissDialogEvents.emit(Unit)
+ fun resetDismissTimeout() {
+ mutableDismissDialogEvents.tryEmit(Unit)
}
private fun updateVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index f78a8dc..876bf2c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -25,6 +25,7 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/** Operates a state of particular slider of the Volume Dialog. */
@@ -37,12 +38,23 @@
) {
val slider: Flow<VolumeDialogStreamModel> =
- volumeDialogStateInteractor.volumeDialogState.mapNotNull {
- it.streamModels[sliderType.audioStream]
- }
+ volumeDialogStateInteractor.volumeDialogState
+ .mapNotNull {
+ it.streamModels[sliderType.audioStream]?.run {
+ if (level < levelMin || level > levelMax) {
+ copy(level = level.coerceIn(levelMin, levelMax))
+ } else {
+ this
+ }
+ }
+ }
+ .distinctUntilChanged()
fun setStreamVolume(userLevel: Int) {
- volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel)
+ with(volumeDialogController) {
+ setStreamVolume(sliderType.audioStream, userLevel)
+ setActiveStream(sliderType.audioStream)
+ }
}
@VolumeDialogScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 25a5f28..5c4d53a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,32 +16,55 @@
package com.android.systemui.volume.dialog.sliders.ui
+import android.animation.Animator
+import android.animation.ObjectAnimator
import android.view.View
-import androidx.lifecycle.viewmodel.compose.viewModel
+import android.view.animation.DecelerateInterpolator
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
+import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.google.android.material.slider.LabelFormatter
+import com.google.android.material.slider.Slider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L
class VolumeDialogSliderViewBinder
@AssistedInject
-constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) {
+constructor(
+ @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel,
+ private val jankListenerFactory: JankListenerFactory,
+) {
fun bind(view: View) {
with(view) {
+ val sliderView: Slider =
+ requireViewById<Slider>(R.id.volume_dialog_slider).apply {
+ labelBehavior = LabelFormatter.LABEL_GONE
+ }
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSliderViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelProvider() },
) { viewModel ->
- setSnapshotBinding {}
+ sliderView.addOnChangeListener { _, value, fromUser ->
+ viewModel.setStreamVolume(value.roundToInt(), fromUser)
+ }
+
+ viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
awaitCancellation()
}
@@ -49,6 +72,19 @@
}
}
+ private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+ with(slider) {
+ valueFrom = levelMin.toFloat()
+ valueTo = levelMax.toFloat()
+ // coerce the current value to the new value range before animating it
+ value = value.coerceIn(valueFrom, valueTo)
+ setValueAnimated(
+ level.toFloat(),
+ jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
+ )
+ }
+ }
+
@AssistedFactory
@VolumeDialogScope
interface Factory {
@@ -58,3 +94,16 @@
): VolumeDialogSliderViewBinder
}
}
+
+private suspend fun Slider.setValueAnimated(
+ newValue: Float,
+ jankListener: Animator.AnimatorListener,
+) {
+ ObjectAnimator.ofFloat(value, newValue)
+ .apply {
+ duration = PROGRESS_CHANGE_ANIMATION_DURATION_MS
+ interpolator = DecelerateInterpolator()
+ addListener(jankListener)
+ }
+ .awaitAnimation<Float> { value = it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 0a00f70..f486fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -16,14 +16,20 @@
package com.android.systemui.volume.dialog.sliders.ui
+import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.compose.ui.util.fastForEachIndexed
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
+import kotlin.math.abs
import kotlinx.coroutines.awaitCancellation
@VolumeDialogScope
@@ -33,17 +39,44 @@
fun bind(view: View) {
with(view) {
+ val volumeDialog: View = requireViewById(R.id.volume_dialog)
+ val floatingSlidersContainer: ViewGroup =
+ requireViewById(R.id.volume_dialog_floating_sliders_container)
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSlidersViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelFactory.create() },
) { viewModel ->
- setSnapshotBinding {}
+ setSnapshotBinding {
+ viewModel.uiModel?.sliderViewBinder?.bind(volumeDialog)
+ val floatingSliderViewBinders =
+ viewModel.uiModel?.floatingSliderViewBinders ?: emptyList()
+ floatingSlidersContainer.ensureChildCount(
+ viewLayoutId = R.layout.volume_dialog_slider_floating,
+ count = floatingSliderViewBinders.size,
+ )
+ floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
+ viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ }
+ }
awaitCancellation()
}
}
}
}
}
+
+private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
+ val childCountDelta = childCount - count
+ when {
+ childCountDelta > 0 -> {
+ removeViews(0, childCountDelta)
+ }
+ childCountDelta < 0 -> {
+ val inflater = LayoutInflater.from(context)
+ repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 7ee722d..ea0b49d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -16,26 +16,85 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+/*
+ This prevents volume slider updates while user interacts with it. This is needed due to the
+ flawed VolumeDialogControllerImpl. It has a single threaded message queue that handles all state
+ updates and doesn't skip sequential updates of the same stream. This leads to a bottleneck when
+ user rigorously adjusts the slider.
+
+ Remove this when getting rid of the VolumeDialogControllerImpl as this doesn't happen in the
+ Volume Panel that uses the new coroutine-backed AudioRepository.
+*/
+// TODO(b/375355785) remove this
+private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
+
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumeDialogSliderViewModel
@AssistedInject
-constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) {
+constructor(
+ @Assisted private val interactor: VolumeDialogSliderInteractor,
+ private val visibilityInteractor: VolumeDialogVisibilityInteractor,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ private val systemClock: SystemClock,
+) {
- val model: Flow<VolumeDialogStreamModel> = interactor.slider
+ private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
- fun setStreamVolume(volume: Int) {
- interactor.setStreamVolume(volume)
+ val model: Flow<VolumeDialogStreamModel> =
+ interactor.slider
+ .filter {
+ val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
+ getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
+
+ init {
+ userVolumeUpdates
+ .filterNotNull()
+ .mapLatest { volume ->
+ interactor.setStreamVolume(volume.newVolumeLevel)
+ Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, model.first().stream, volume)
+ }
+ .launchIn(coroutineScope)
}
+ fun setStreamVolume(volume: Int, fromUser: Boolean) {
+ if (fromUser) {
+ visibilityInteractor.resetDismissTimeout()
+ userVolumeUpdates.value =
+ VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis())
+ }
+ }
+
+ private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
+
@AssistedFactory
interface Factory {
fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
}
+
+ private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index b5b292f..22cf89f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import androidx.compose.runtime.getValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
@@ -24,10 +27,9 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -39,9 +41,10 @@
private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
-) {
+) : ExclusiveActivatable() {
- val sliders: Flow<VolumeDialogSliderUiModel> =
+ private val hydrator = Hydrator("VolumeDialogSlidersViewModel")
+ private val slidersStateFlow: StateFlow<VolumeDialogSliderUiModel?> =
slidersInteractor.sliders
.distinctUntilChanged()
.map { slidersModel ->
@@ -52,7 +55,13 @@
)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
- .filterNotNull()
+
+ val uiModel: VolumeDialogSliderUiModel? by
+ hydrator.hydratedStateOf("VolumeDialogSlidersViewModel#uiModel", slidersStateFlow)
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
sliderViewBinderFactory.create {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
index 77733fe..eb9483f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
@@ -20,6 +20,7 @@
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
+import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
@@ -27,6 +28,7 @@
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -40,6 +42,7 @@
constructor(
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogViewBinder: VolumeDialogViewBinder,
+ private val slidersViewBinder: VolumeDialogSlidersViewBinder,
private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
private val gravityViewModel: VolumeDialogGravityViewModel,
) {
@@ -50,11 +53,11 @@
dialog.setContentView(R.layout.volume_dialog)
dialog.setCanceledOnTouchOutside(true)
- settingsButtonViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_settings))
- volumeDialogViewBinder.bind(
- dialog,
- dialog.requireViewById(R.id.volume_dialog_container),
- )
+ with(dialog.requireViewById<View>(R.id.volume_dialog_container)) {
+ slidersViewBinder.bind(this)
+ settingsButtonViewBinder.bind(this)
+ volumeDialogViewBinder.bind(dialog, this)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index d01c1ca..e26a7ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS;
import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -60,7 +61,6 @@
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -633,7 +633,7 @@
@Test
@EnableFlags({
FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- StatusBarNotifChips.FLAG_NAME,
+ FLAG_STATUS_BAR_RON_CHIPS,
FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() {
resumeAndGetFragment();
@@ -660,8 +660,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
+ @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -673,7 +673,7 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
resumeAndGetFragment();
@@ -689,8 +689,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
+ @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -705,9 +705,9 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
+ public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -724,8 +724,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
+ @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -740,9 +740,9 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
+ public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -759,8 +759,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
+ @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() {
resumeAndGetFragment();
// Ongoing activity started
@@ -781,9 +781,9 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
+ public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() {
resumeAndGetFragment();
// Ongoing activity started
@@ -804,7 +804,7 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
public void secondaryOngoingActivityEnded_chipHidden() {
resumeAndGetFragment();
@@ -828,8 +828,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
+ @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -846,9 +846,9 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -866,8 +866,8 @@
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
+ @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -898,9 +898,9 @@
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index b45120e..43eb93e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -43,30 +43,36 @@
private var state = VolumeDialogController.State()
override fun setActiveStream(stream: Int) {
- // ensure streamState existence for the active stream
- state.states.getOrElse(stream) {
- VolumeDialogController.StreamState().also { streamState ->
- state.states.put(stream, streamState)
- }
- }
- state.activeStream = stream
- }
-
- override fun setStreamVolume(stream: Int, userLevel: Int) {
- val streamState =
- state.states.getOrElse(stream) {
+ updateState {
+ // ensure streamState existence for the active stream`
+ states.getOrElse(stream) {
VolumeDialogController.StreamState().also { streamState ->
state.states.put(stream, streamState)
}
}
- streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+ activeStream = stream
+ }
+ }
+
+ override fun setStreamVolume(stream: Int, userLevel: Int) {
+ updateState {
+ val streamState =
+ states.getOrElse(stream) {
+ VolumeDialogController.StreamState().also { streamState ->
+ states.put(stream, streamState)
+ }
+ }
+ streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+ }
}
override fun setRingerMode(ringerModeNormal: Int, external: Boolean) {
- if (external) {
- state.ringerModeExternal = ringerModeNormal
- } else {
- state.ringerModeInternal = ringerModeNormal
+ updateState {
+ if (external) {
+ ringerModeExternal = ringerModeNormal
+ } else {
+ ringerModeInternal = ringerModeNormal
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
similarity index 85%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
index 2316a2f..c0d65a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
import android.content.packageManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.util.time.fakeSystemClock
-val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by
+val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by
Kosmos.Fixture {
- DemoNotifChipViewModel(
+ DemoRonChipViewModel(
commandRegistry = commandRegistry,
packageManager = packageManager,
systemClock = fakeSystemClock,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/ui/viewmodel/NotifChipsViewModelKosmos.kt
similarity index 85%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/ui/viewmodel/NotifChipsViewModelKosmos.kt
index af24c37..11d1cb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+package com.android.systemui.statusbar.chips.ron.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
val Kosmos.notifChipsViewModel: NotifChipsViewModel by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 0300bf4..b2be0b2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -20,8 +20,8 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
-import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.ron.ui.viewmodel.notifChipsViewModel
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.statusBarChipsLogger
@@ -35,7 +35,7 @@
castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
callChipViewModel = callChipViewModel,
notifChipsViewModel = notifChipsViewModel,
- demoNotifChipViewModel = demoNotifChipViewModel,
+ demoRonChipViewModel = demoRonChipViewModel,
logger = statusBarChipsLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
new file mode 100644
index 0000000..423100a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.volume.dialog.sliders.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+
+val Kosmos.volumeDialogSliderInteractor: VolumeDialogSliderInteractor by
+ Kosmos.Fixture {
+ VolumeDialogSliderInteractor(
+ volumeDialogSliderType,
+ volumeDialogStateInteractor,
+ volumeDialogController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
index af24c37..cc8c1ea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+package com.android.systemui.volume.dialog.sliders.domain.model
+import android.media.AudioManager
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-val Kosmos.notifChipsViewModel: NotifChipsViewModel by
- Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) }
+var Kosmos.volumeDialogSliderType: VolumeDialogSliderType by
+ Kosmos.Fixture { VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM) }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0ca3b56..679c7ac 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -112,6 +112,7 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.expresslog.Histogram;
@@ -1226,7 +1227,17 @@
// been re-enabled (after being updated for example), then we just overwrite the old
// values.
for (Entry<String, Integer> entry : knownAuth.entrySet()) {
- accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
+ String type = entry.getKey();
+ Integer newUid = entry.getValue();
+ if (!Objects.equals(metaAuthUid.get(type), newUid)) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ type,
+ newUid,
+ FrameworkStatsLog
+ .ACCOUNT_MANAGER_EVENT__EVENT_TYPE__AUTHENTICATOR_ADDED);
+ }
+ accountsDb.insertOrReplaceMetaAuthTypeAndUid(type, newUid);
}
final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
@@ -1945,6 +1956,11 @@
}
accounts.accountsDb.setTransactionSuccessful();
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_ADDED);
logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
accountId,
accounts, callingUid);
@@ -2544,6 +2560,11 @@
}
String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
: AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_REMOVED);
logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 5ac883c..9c24abc 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -316,6 +316,8 @@
/* letsPersonalDataIntoProfile= */ true)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
.addAction(MediaStore.ACTION_VIDEO_CAPTURE)
.addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
@@ -438,6 +440,8 @@
/* letsPersonalDataIntoProfile= */ false)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
.addAction(MediaStore.ACTION_VIDEO_CAPTURE)
.addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6a808b..a59f4bd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -39,6 +39,7 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -87,6 +88,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelableException;
+import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
@@ -314,6 +316,8 @@
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
+ super(PermissionEnforcer.fromContext(context));
+
mContext = context;
mPm = pm;
@@ -1877,23 +1881,20 @@
}
@Override
+ @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.VERIFICATION_AGENT permission "
- + "to get the verification policy");
- }
+ getVerificationPolicy_enforcePermission();
return mVerificationPolicy.get();
}
@Override
+ @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.VERIFICATION_AGENT permission "
- + "to set the verification policy");
+ setVerificationPolicy_enforcePermission();
+ final int callingUid = getCallingUid();
+ // Only the verifier currently bound by the system can change the policy, except for Shell
+ if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
+ mVerifierController.assertCallerIsCurrentVerifier(callingUid);
}
if (!isValidVerificationPolicy(policy)) {
return false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9a9e434..512b195 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2884,14 +2884,13 @@
}
// Send the request to the verifier and wait for its response before the rest of
// the installation can proceed.
+ final VerifierCallback verifierCallback = new VerifierCallback();
if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId,
sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
- new VerifierCallback(), /* retry= */ false)) {
- // A verifier is installed but cannot be connected. Installation disallowed.
- onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "A verifier agent is available on device but cannot be connected.",
- /* extras= */ null);
+ verifierCallback, /* retry= */ false)) {
+ // A verifier is installed but cannot be connected.
+ verifierCallback.onConnectionFailed();
}
} else {
// No need to check with verifier. Proceed with the rest of the verification.
@@ -2995,7 +2994,6 @@
onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
"A verifier agent is available on device but cannot be connected.",
bundle);
-
});
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4652c3a..f8e56e1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -399,6 +399,10 @@
return runUnarchive();
case "get-domain-verification-agent":
return runGetDomainVerificationAgent();
+ case "get-verification-policy":
+ return runGetVerificationPolicy();
+ case "set-verification-policy":
+ return runSetVerificationPolicy();
default: {
if (ART_SERVICE_COMMANDS.contains(cmd)) {
return runArtServiceCommand();
@@ -4645,6 +4649,86 @@
return 0;
}
+ private int runGetVerificationPolicy() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_SYSTEM, "runGetVerificationPolicy");
+ try {
+ final IPackageInstaller installer = mInterface.getPackageInstaller();
+ // TODO(b/360129657): global verification policy should be per user
+ final int policy = installer.getVerificationPolicy();
+ pw.println(policy);
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runSetVerificationPolicy() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ final String policyStr = getNextArg();
+ if (policyStr == null) {
+ pw.println("Error: policy not specified");
+ return 1;
+ }
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_SYSTEM, "runSetVerificationPolicy");
+ try {
+ final IPackageInstaller installer = mInterface.getPackageInstaller();
+ // TODO(b/360129657): global verification policy should be per user
+ final boolean success = installer.setVerificationPolicy(Integer.parseInt(policyStr));
+ if (!success) {
+ pw.println("Failure setting verification policy.");
+ return 1;
+ }
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -5073,6 +5157,14 @@
pw.println(" --user: return the agent of the given user (SYSTEM_USER if unspecified)");
pw.println(" get-package-storage-stats [--user <USER_ID>] <PACKAGE>");
pw.println(" Return the storage stats for the given app, if present");
+ pw.println(" get-verification-policy [--user USER_ID]");
+ pw.println(" Display current verification enforcement policy which will be applied to");
+ pw.println(" all the future installation sessions");
+ pw.println(" --user: show the policy of the given user (SYSTEM_USER if unspecified)");
+ pw.println(" set-verification-policy POLICY [--user USER_ID]");
+ pw.println(" Sets the verification policy of all the future installation sessions.");
+ pw.println(" --user: set the policy of the given user (SYSTEM_USER if unspecified)");
+ pw.println("");
pw.println("");
printArtServiceHelp();
pw.println("");
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index a35618b..78849d2 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -18,13 +18,12 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
@@ -114,10 +113,17 @@
private final Context mContext;
private final Handler mHandler;
+ // Guards the remote service object, as well as the verifier name and UID, which should all be
+ // changed at the same time.
+ private final Object mLock = new Object();
@Nullable
+ @GuardedBy("mLock")
private ServiceConnector<IVerifierService> mRemoteService;
@Nullable
+ @GuardedBy("mLock")
private ComponentName mRemoteServiceComponentName;
+ @GuardedBy("mLock")
+ private int mRemoteServiceUid = INVALID_UID;
@NonNull
private Injector mInjector;
@@ -143,9 +149,11 @@
*/
@Nullable
public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) {
- if (isVerifierConnected()) {
- // Verifier is connected or is being connected, so it must be installed.
- return mRemoteServiceComponentName.getPackageName();
+ synchronized (mLock) {
+ if (isVerifierConnectedLocked()) {
+ // Verifier is connected or is being connected, so it must be installed.
+ return mRemoteServiceComponentName.getPackageName();
+ }
}
// Verifier has been disconnected, or it hasn't been connected. Check if it's installed.
return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId);
@@ -178,16 +186,29 @@
}
return true;
}
+ Computer snapshot = snapshotSupplier.get();
Pair<ServiceConnector<IVerifierService>, ComponentName> result =
- mInjector.getRemoteService(snapshotSupplier.get(), mContext, userId, mHandler);
+ mInjector.getRemoteService(snapshot, mContext, userId, mHandler);
if (result == null || result.first == null) {
if (DEBUG) {
Slog.i(TAG, "Unable to find a qualified verifier.");
}
return false;
}
- mRemoteService = result.first;
- mRemoteServiceComponentName = result.second;
+ final int verifierUid = snapshot.getPackageUidInternal(
+ result.second.getPackageName(), 0, userId, /* callingUid= */ SYSTEM_UID);
+ if (verifierUid == INVALID_UID) {
+ if (DEBUG) {
+ Slog.i(TAG, "Unable to find the UID of the qualified verifier.");
+ }
+ return false;
+ }
+ synchronized (mLock) {
+ mRemoteService = result.first;
+ mRemoteServiceComponentName = result.second;
+ mRemoteServiceUid = verifierUid;
+ }
+
if (DEBUG) {
Slog.i(TAG, "Connecting to a qualified verifier: " + mRemoteServiceComponentName);
}
@@ -212,10 +233,13 @@
}
private void destroy() {
- if (isVerifierConnected()) {
- mRemoteService.unbind();
- mRemoteService = null;
- mRemoteServiceComponentName = null;
+ synchronized (mLock) {
+ if (isVerifierConnectedLocked()) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ mRemoteServiceComponentName = null;
+ mRemoteServiceUid = INVALID_UID;
+ }
}
}
});
@@ -223,7 +247,8 @@
return true;
}
- private boolean isVerifierConnected() {
+ @GuardedBy("mLock")
+ private boolean isVerifierConnectedLocked() {
return mRemoteService != null && mRemoteServiceComponentName != null;
}
@@ -232,19 +257,21 @@
* requested for verification.
*/
public void notifyPackageNameAvailable(@NonNull String packageName) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+ }
+ return;
}
- return;
+ // Best effort. We don't check for the result.
+ mRemoteService.run(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying package name available for " + packageName);
+ }
+ service.onPackageNameAvailable(packageName);
+ });
}
- // Best effort. We don't check for the result.
- mRemoteService.run(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying package name available for " + packageName);
- }
- service.onPackageNameAvailable(packageName);
- });
}
/**
@@ -253,27 +280,29 @@
* will no longer be requested for verification, possibly because the installation is canceled.
*/
public void notifyVerificationCancelled(@NonNull String packageName) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+ }
+ return;
}
- return;
+ // Best effort. We don't check for the result.
+ mRemoteService.run(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification cancelled for " + packageName);
+ }
+ service.onVerificationCancelled(packageName);
+ });
}
- // Best effort. We don't check for the result.
- mRemoteService.run(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying verification cancelled for " + packageName);
- }
- service.onVerificationCancelled(packageName);
- });
}
/**
* Called to notify the bound verifier agent that a package that's pending installation needs
* to be verified right now.
* <p>The verification request must be sent to the verifier as soon as the verifier is
- * connected. If the connection cannot be made within {@link #CONNECTION_TIMEOUT_SECONDS}</p>
- * of when the request is sent out, we consider the verification to be failed and notify the
+ * connected. If the connection cannot be made within the specified time limit from
+ * when the request is sent out, we consider the verification to be failed and notify the
* installation session.</p>
* <p>If a response is not returned from the verifier agent within a timeout duration from the
* time the request is sent to the verifier, the verification will be considered a failure.</p>
@@ -291,43 +320,48 @@
if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) {
return false;
}
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
- }
- // Normally this should not happen because we just tried to bind. But if the verifier
- // just crashed or just became unavailable, we should notify the installation session so
- // it can finish with a verification failure.
- return false;
- }
// For now, the verification id is the same as the installation session id.
final int verificationId = installationSessionId;
- final VerificationSession session = new VerificationSession(
- /* id= */ verificationId,
- /* installSessionId= */ installationSessionId,
- packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
- verificationPolicy, new VerificationSessionInterface(callback));
- AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
- if (!retry) {
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
if (DEBUG) {
- Slog.i(TAG, "Notifying verification required for session " + verificationId);
+ Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
}
- service.onVerificationRequired(session);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Notifying verification retry for session " + verificationId);
+ // Normally this should not happen because we just tried to bind. But if the
+ // verifier just crashed or just became unavailable, we should notify the
+ // installation session so it can finish with a verification failure.
+ return false;
+ }
+ final VerificationSession session = new VerificationSession(
+ /* id= */ verificationId,
+ /* installSessionId= */ installationSessionId,
+ packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
+ verificationPolicy, new VerificationSessionInterface(callback));
+ AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+ if (!retry) {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification required for session "
+ + verificationId);
+ }
+ service.onVerificationRequired(session);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification retry for session "
+ + verificationId);
+ }
+ service.onVerificationRetry(session);
}
- service.onVerificationRetry(session);
- }
- }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
- .whenComplete((res, err) -> {
- if (err != null) {
- Slog.e(TAG, "Error notifying verification request for session " + verificationId,
- err);
- // Notify the installation session so it can finish with verification failure.
- callback.onConnectionFailed();
- }
- });
+ }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+ .whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Error notifying verification request for session "
+ + verificationId, err);
+ // Notify the installation session so it can finish with verification
+ // failure.
+ callback.onConnectionFailed();
+ }
+ });
+ }
// Keep track of the session status with the ID. Start counting down the session timeout.
final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis();
final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis();
@@ -369,24 +403,27 @@
* Called to notify the bound verifier agent that a verification request has timed out.
*/
public void notifyVerificationTimeout(int verificationId) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG,
- "Verifier is not connected. Not notifying timeout for " + verificationId);
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "Verifier is not connected. Not notifying timeout for "
+ + verificationId);
+ }
+ return;
}
- return;
+ AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying timeout for " + verificationId);
+ }
+ service.onVerificationTimeout(verificationId);
+ }).whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Error notifying VerificationTimeout for session "
+ + verificationId, err);
+ }
+ });
}
- AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying timeout for " + verificationId);
- }
- service.onVerificationTimeout(verificationId);
- }).whenComplete((res, err) -> {
- if (err != null) {
- Slog.e(TAG, "Error notifying VerificationTimeout for session "
- + verificationId, (Throwable) err);
- }
- });
}
/**
@@ -405,17 +442,19 @@
}
}
- @RequiresPermission(Manifest.permission.VERIFICATION_AGENT)
- private void checkCallerPermission() {
- // TODO: think of a better way to test it on non-eng builds
- if (Build.IS_ENG) {
- return;
- }
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the"
- + " com.android.permission.VERIFICATION_AGENT permission"
- + " to use VerificationSession APIs.");
+ /**
+ * Assert that the calling UID is the same as the UID of the currently connected verifier.
+ */
+ public void assertCallerIsCurrentVerifier(int callingUid) {
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ throw new IllegalStateException(
+ "Unable to proceed because the verifier has been disconnected.");
+ }
+ if (callingUid != mRemoteServiceUid) {
+ throw new IllegalStateException(
+ "Calling uid " + callingUid + " is not the current verifier.");
+ }
}
}
@@ -429,7 +468,7 @@
@Override
public long getTimeoutTime(int verificationId) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -442,7 +481,7 @@
@Override
public long extendTimeRemaining(int verificationId, long additionalMs) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -456,7 +495,7 @@
@Override
public boolean setVerificationPolicy(int verificationId,
@PackageInstaller.VerificationPolicy int policy) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -469,7 +508,7 @@
@Override
public void reportVerificationIncomplete(int id, int reason) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
final VerificationStatusTracker tracker;
synchronized (mVerificationStatus) {
tracker = mVerificationStatus.get(id);
@@ -484,15 +523,9 @@
}
@Override
- public void reportVerificationComplete(int id, VerificationStatus verificationStatus) {
- reportVerificationCompleteWithExtensionResponse(id, verificationStatus,
- /* extensionResponse= */ null);
- }
-
- @Override
- public void reportVerificationCompleteWithExtensionResponse(int id,
- VerificationStatus verificationStatus, PersistableBundle extensionResponse) {
- checkCallerPermission();
+ public void reportVerificationComplete(int id, VerificationStatus verificationStatus,
+ @Nullable PersistableBundle extensionResponse) {
+ assertCallerIsCurrentVerifier(getCallingUid());
final VerificationStatusTracker tracker;
synchronized (mVerificationStatus) {
tracker = mVerificationStatus.get(id);
diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/forensic/DataAggregator.java
new file mode 100644
index 0000000..0079818
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/DataAggregator.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 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.security.forensic;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.security.forensic.ForensicEvent;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DataAggregator {
+ private static final String TAG = "Forensic DataAggregator";
+ private static final int MSG_SINGLE_DATA = 0;
+ private static final int MSG_BATCH_DATA = 1;
+ private static final int MSG_DISABLE = 2;
+
+ private static final int STORED_EVENTS_SIZE_LIMIT = 1024;
+ private final ForensicService mForensicService;
+ private final ArrayList<DataSource> mDataSources;
+
+ private List<ForensicEvent> mStoredEvents = new ArrayList<>();
+ private ServiceThread mHandlerThread;
+ private Handler mHandler;
+ public DataAggregator(ForensicService forensicService) {
+ mForensicService = forensicService;
+ mDataSources = new ArrayList<DataSource>();
+ }
+
+ @VisibleForTesting
+ void setHandler(Looper looper, ServiceThread serviceThread) {
+ mHandlerThread = serviceThread;
+ mHandler = new EventHandler(looper, this);
+ }
+
+ /**
+ * Initialize DataSources
+ * @return Whether the initialization succeeds.
+ */
+ // TODO: Add the corresponding data sources
+ public boolean initialize() {
+ return true;
+ }
+
+ /**
+ * Enable the data collection of all DataSources.
+ */
+ public void enable() {
+ mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND,
+ /* allowIo */ false);
+ mHandlerThread.start();
+ mHandler = new EventHandler(mHandlerThread.getLooper(), this);
+ for (DataSource ds : mDataSources) {
+ ds.enable();
+ }
+ }
+
+ /**
+ * DataSource calls it to transmit a single event.
+ */
+ public void addSingleData(ForensicEvent event) {
+ mHandler.obtainMessage(MSG_SINGLE_DATA, event).sendToTarget();
+ }
+
+ /**
+ * DataSource calls it to transmit list of events.
+ */
+ public void addBatchData(List<ForensicEvent> events) {
+ mHandler.obtainMessage(MSG_BATCH_DATA, events).sendToTarget();
+ }
+
+ /**
+ * Disable the data collection of all DataSources.
+ */
+ public void disable() {
+ mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
+ }
+
+ private void onNewSingleData(ForensicEvent event) {
+ if (mStoredEvents.size() < STORED_EVENTS_SIZE_LIMIT) {
+ mStoredEvents.add(event);
+ } else {
+ mForensicService.addNewData(mStoredEvents);
+ mStoredEvents = new ArrayList<>();
+ }
+ }
+
+ private void onNewBatchData(List<ForensicEvent> events) {
+ mForensicService.addNewData(events);
+ }
+
+ private void onDisable() {
+ for (DataSource ds : mDataSources) {
+ ds.disable();
+ }
+ mHandlerThread.quitSafely();
+ mHandlerThread = null;
+ }
+
+ private static class EventHandler extends Handler {
+ private final DataAggregator mDataAggregator;
+ EventHandler(Looper looper, DataAggregator dataAggregator) {
+ super(looper);
+ mDataAggregator = dataAggregator;
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SINGLE_DATA:
+ mDataAggregator.onNewSingleData((ForensicEvent) msg.obj);
+ break;
+ case MSG_BATCH_DATA:
+ mDataAggregator.onNewBatchData((List<ForensicEvent>) msg.obj);
+ break;
+ case MSG_DISABLE:
+ mDataAggregator.onDisable();
+ break;
+ default:
+ Slog.w(TAG, "Unknown message: " + msg.what);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/services/core/java/com/android/server/security/forensic/DataSource.java
similarity index 63%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
copy to services/core/java/com/android/server/security/forensic/DataSource.java
index af24c37..da7ee21 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
+++ b/services/core/java/com/android/server/security/forensic/DataSource.java
@@ -14,10 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.notification.ui.viewmodel
+package com.android.server.security.forensic;
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+public interface DataSource {
+ /**
+ * Enable the data collection.
+ */
+ void enable();
-val Kosmos.notifChipsViewModel: NotifChipsViewModel by
- Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) }
+ /**
+ * Disable the data collection.
+ */
+ void disable();
+}
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 20c648e..53b07c0 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -22,6 +22,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicService;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
@@ -32,6 +33,7 @@
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.List;
/**
* @hide
@@ -64,6 +66,7 @@
private final Context mContext;
private final Handler mHandler;
private final BackupTransportConnection mBackupTransportConnection;
+ private final DataAggregator mDataAggregator;
private final BinderService mBinderService;
private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
@@ -79,6 +82,7 @@
mContext = injector.getContext();
mHandler = new EventHandler(injector.getLooper(), this);
mBackupTransportConnection = injector.getBackupTransportConnection();
+ mDataAggregator = injector.getDataAggregator(this);
mBinderService = new BinderService(this);
}
@@ -167,6 +171,9 @@
Slog.e(TAG, "RemoteException", e);
}
break;
+ case MSG_BACKUP:
+ mService.backup((List<ForensicEvent>) msg.obj);
+ break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
@@ -192,6 +199,10 @@
private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
switch (mState) {
case STATE_INVISIBLE:
+ if (!mDataAggregator.initialize()) {
+ callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE);
+ break;
+ }
mState = STATE_VISIBLE;
notifyStateMonitors();
callback.onSuccess();
@@ -227,6 +238,7 @@
callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
break;
}
+ mDataAggregator.enable();
mState = STATE_ENABLED;
notifyStateMonitors();
callback.onSuccess();
@@ -243,6 +255,7 @@
switch (mState) {
case STATE_ENABLED:
mBackupTransportConnection.release();
+ mDataAggregator.disable();
mState = STATE_VISIBLE;
notifyStateMonitors();
callback.onSuccess();
@@ -255,6 +268,17 @@
}
}
+ /**
+ * Add a list of ForensicEvent.
+ */
+ public void addNewData(List<ForensicEvent> events) {
+ mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget();
+ }
+
+ private void backup(List<ForensicEvent> events) {
+ mBackupTransportConnection.addData(events);
+ }
+
@Override
public void onStart() {
try {
@@ -275,6 +299,8 @@
Looper getLooper();
BackupTransportConnection getBackupTransportConnection();
+
+ DataAggregator getDataAggregator(ForensicService forensicService);
}
private static final class InjectorImpl implements Injector {
@@ -303,6 +329,11 @@
public BackupTransportConnection getBackupTransportConnection() {
return new BackupTransportConnection(mContext);
}
+
+ @Override
+ public DataAggregator getDataAggregator(ForensicService forensicService) {
+ return new DataAggregator(forensicService);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 6a1f28d..fa678e2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -242,6 +242,8 @@
static {
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
Manifest.permission.CAMERA);
+ ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_MOTION_PHOTO_CAPTURE,
+ Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index 2461798..3046d4b 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
@@ -122,6 +124,10 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ // Mock that the UID of this test becomes the UID of the verifier
+ when(mSnapshot.getPackageUidInternal(anyString(), anyLong(), anyInt(), anyInt()))
+ .thenReturn(InstrumentationRegistry.getInstrumentation().getContext()
+ .getApplicationInfo().uid);
when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(
TEST_VERIFIER_COMPONENT_NAME.getPackageName());
when(mInjector.getRemoteService(
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 2b55303..feb00e7 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -19,23 +19,35 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
+import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
+import android.util.ArrayMap;
+
+import com.android.server.ServiceThread;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
public class ForensicServiceTest {
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
@@ -55,10 +67,12 @@
@Mock
private Context mContext;
private BackupTransportConnection mBackupTransportConnection;
-
+ private DataAggregator mDataAggregator;
private ForensicService mForensicService;
private TestLooper mTestLooper;
private Looper mLooper;
+ private TestLooper mTestLooperOfDataAggregator;
+ private Looper mLooperOfDataAggregator;
@SuppressLint("VisibleForTests")
@Before
@@ -67,6 +81,8 @@
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
+ mTestLooperOfDataAggregator = new TestLooper();
+ mLooperOfDataAggregator = mTestLooperOfDataAggregator.getLooper();
mForensicService = new ForensicService(new MockInjector(mContext));
mForensicService.onStart();
}
@@ -121,6 +137,8 @@
assertEquals(STATE_INVISIBLE, scb1.mState);
assertEquals(STATE_INVISIBLE, scb2.mState);
+ doReturn(true).when(mDataAggregator).initialize();
+
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().makeVisible(ccb);
mTestLooper.dispatchAll();
@@ -130,6 +148,29 @@
}
@Test
+ public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable()
+ throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ doReturn(false).when(mDataAggregator).initialize();
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue());
+ }
+
+ @Test
public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
mForensicService.setState(STATE_VISIBLE);
StateCallback scb1 = new StateCallback();
@@ -262,6 +303,8 @@
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
+
+ verify(mDataAggregator, times(1)).enable();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
assertNull(ccb.mErrorCode);
@@ -361,14 +404,67 @@
doNothing().when(mBackupTransportConnection).release();
+ ServiceThread mockThread = spy(ServiceThread.class);
+ mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().disable(ccb);
mTestLooper.dispatchAll();
+ mTestLooperOfDataAggregator.dispatchAll();
+ // TODO: We can verify the data sources once we implement them.
+ verify(mockThread, times(1)).quitSafely();
assertEquals(STATE_VISIBLE, scb1.mState);
assertEquals(STATE_VISIBLE, scb2.mState);
assertNull(ccb.mErrorCode);
}
+ @Test
+ public void testDataAggregator_AddBatchData() {
+ mForensicService.setState(STATE_ENABLED);
+ ServiceThread mockThread = spy(ServiceThread.class);
+ mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
+ String eventOneType = "event_one_type";
+ String eventOneMapKey = "event_one_map_key";
+ String eventOneMapVal = "event_one_map_val";
+ Map<String, String> eventOneMap = new ArrayMap<String, String>();
+ eventOneMap.put(eventOneMapKey, eventOneMapVal);
+ ForensicEvent eventOne = new ForensicEvent(eventOneType, eventOneMap);
+
+ String eventTwoType = "event_two_type";
+ String eventTwoMapKey = "event_two_map_key";
+ String eventTwoMapVal = "event_two_map_val";
+ Map<String, String> eventTwoMap = new ArrayMap<String, String>();
+ eventTwoMap.put(eventTwoMapKey, eventTwoMapVal);
+ ForensicEvent eventTwo = new ForensicEvent(eventTwoType, eventTwoMap);
+
+ List<ForensicEvent> events = new ArrayList<>();
+ events.add(eventOne);
+ events.add(eventTwo);
+
+ doReturn(true).when(mBackupTransportConnection).addData(any());
+
+ mDataAggregator.addBatchData(events);
+ mTestLooperOfDataAggregator.dispatchAll();
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class);
+ verify(mBackupTransportConnection).addData(captor.capture());
+ List<ForensicEvent> receivedEvents = captor.getValue();
+ assertEquals(receivedEvents.size(), 2);
+
+ assertEquals(receivedEvents.getFirst().getType(), eventOneType);
+ assertEquals(receivedEvents.getFirst().getKeyValuePairs().size(), 1);
+ assertEquals(receivedEvents.getFirst().getKeyValuePairs().get(eventOneMapKey),
+ eventOneMapVal);
+
+ assertEquals(receivedEvents.getLast().getType(), eventTwoType);
+ assertEquals(receivedEvents.getLast().getKeyValuePairs().size(), 1);
+ assertEquals(receivedEvents.getLast().getKeyValuePairs().get(eventTwoMapKey),
+ eventTwoMapVal);
+
+ }
+
private class MockInjector implements ForensicService.Injector {
private final Context mContext;
@@ -393,6 +489,11 @@
return mBackupTransportConnection;
}
+ @Override
+ public DataAggregator getDataAggregator(ForensicService forensicService) {
+ mDataAggregator = spy(new DataAggregator(forensicService));
+ return mDataAggregator;
+ }
}
private static class StateCallback extends IForensicServiceStateCallback.Stub {