Merge "Only provide DesktopTaskChangeListener if the flag is enabled." into main
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 35ac3ee..879c7a2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8542,11 +8542,14 @@
method public long getAudioHandle();
method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public long getAvDataId();
+ method @FlaggedApi("android.media.tv.flags.tuner_w_apis") public int getDataGroupId();
method public long getDataLength();
method public long getDts();
method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
+ method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @IntRange(from=0) public int getIndexInDataGroup();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method @IntRange(from=0) public int getMpuSequenceNumber();
+ method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @IntRange(from=0) public int getNumDataPieces();
method public long getOffset();
method public long getPts();
method public int getScIndexMask();
diff --git a/core/java/android/hardware/DisplayLuts.java b/core/java/android/hardware/DisplayLuts.java
new file mode 100644
index 0000000..b162ad6
--- /dev/null
+++ b/core/java/android/hardware/DisplayLuts.java
@@ -0,0 +1,131 @@
+/*
+ * 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 android.hardware;
+
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class DisplayLuts {
+ private IntArray mOffsets;
+ private int mTotalLength;
+
+ private List<float[]> mLutBuffers;
+ private IntArray mLutDimensions;
+ private IntArray mLutSizes;
+ private IntArray mLutSamplingKeys;
+ private static final int LUT_LENGTH_LIMIT = 100000;
+
+ public DisplayLuts() {
+ mOffsets = new IntArray();
+ mTotalLength = 0;
+
+ mLutBuffers = new ArrayList<>();
+ mLutDimensions = new IntArray();
+ mLutSizes = new IntArray();
+ mLutSamplingKeys = new IntArray();
+ }
+
+ /**
+ * Add the lut to be applied.
+ *
+ * @param buffer
+ * @param dimension either 1D or 3D
+ * @param size
+ * @param samplingKey
+ */
+ public void addLut(@NonNull float[] buffer, @LutProperties.Dimension int dimension,
+ int size, @LutProperties.SamplingKey int samplingKey) {
+
+ int lutLength = 0;
+ if (dimension == LutProperties.ONE_DIMENSION) {
+ lutLength = size;
+ } else if (dimension == LutProperties.THREE_DIMENSION) {
+ lutLength = size * size * size;
+ } else {
+ clear();
+ throw new IllegalArgumentException("The dimension is either 1D or 3D!");
+ }
+
+ if (lutLength >= LUT_LENGTH_LIMIT) {
+ clear();
+ throw new IllegalArgumentException("The lut length is too big to handle!");
+ }
+
+ mOffsets.add(mTotalLength);
+ mTotalLength += lutLength;
+
+ mLutBuffers.add(buffer);
+ mLutDimensions.add(dimension);
+ mLutSizes.add(size);
+ mLutSamplingKeys.add(samplingKey);
+ }
+
+ private void clear() {
+ mTotalLength = 0;
+ mOffsets.clear();
+ mLutBuffers.clear();
+ mLutDimensions.clear();
+ mLutSamplingKeys.clear();
+ }
+
+ /**
+ * @return the array of Lut buffers
+ */
+ public float[] getLutBuffers() {
+ float[] buffer = new float[mTotalLength];
+
+ for (int i = 0; i < mLutBuffers.size(); i++) {
+ float[] lutBuffer = mLutBuffers.get(i);
+ System.arraycopy(lutBuffer, 0, buffer, mOffsets.get(i), lutBuffer.length);
+ }
+ return buffer;
+ }
+
+ /**
+ * @return the starting point of each lut memory region of the lut buffer
+ */
+ public int[] getOffsets() {
+ return mOffsets.toArray();
+ }
+
+ /**
+ * @return the array of Lut size
+ */
+ public int[] getLutSizes() {
+ return mLutSizes.toArray();
+ }
+
+ /**
+ * @return the array of Lut dimension
+ */
+ public int[] getLutDimensions() {
+ return mLutDimensions.toArray();
+ }
+
+ /**
+ * @return the array of sampling key
+ */
+ public int[] getLutSamplingKeys() {
+ return mLutSamplingKeys.toArray();
+ }
+}
diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java
new file mode 100644
index 0000000..57f8a4e
--- /dev/null
+++ b/core/java/android/hardware/LutProperties.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.hardware;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Lut properties class.
+ *
+ * A Lut (Look-Up Table) is a pre-calculated table for color transformation.
+ *
+ * @hide
+ */
+public final class LutProperties {
+ private final @Dimension int mDimension;
+ private final long mSize;
+ private final @SamplingKey int[] mSamplingKeys;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SAMPLING_KEY_"}, value = {
+ SAMPLING_KEY_RGB,
+ SAMPLING_KEY_MAX_RGB
+ })
+ public @interface SamplingKey {
+ }
+
+ /** use r,g,b channel as the gain value of a Lut */
+ public static final int SAMPLING_KEY_RGB = 0;
+
+ /** use max of r,g,b channel as the gain value of a Lut */
+ public static final int SAMPLING_KEY_MAX_RGB = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ ONE_DIMENSION,
+ THREE_DIMENSION
+ })
+ public @interface Dimension {
+ }
+
+ /** The Lut is one dimensional */
+ public static final int ONE_DIMENSION = 1;
+
+ /** The Lut is three dimensional */
+ public static final int THREE_DIMENSION = 3;
+
+ public @Dimension int getDimension() {
+ return mDimension;
+ }
+
+ /**
+ * @return the size of the Lut.
+ */
+ public long getSize() {
+ return mSize;
+ }
+
+ /**
+ * @return the list of sampling keys
+ */
+ public @SamplingKey int[] getSamplingKeys() {
+ if (mSamplingKeys.length == 0) {
+ throw new IllegalStateException("no sampling key!");
+ }
+ return mSamplingKeys;
+ }
+
+ /* use in the native code */
+ private LutProperties(@Dimension int dimension, long size, @SamplingKey int[] samplingKeys) {
+ if (dimension != ONE_DIMENSION || dimension != THREE_DIMENSION) {
+ throw new IllegalArgumentException("The dimension is either 1 or 3!");
+ }
+ mDimension = dimension;
+ mSize = size;
+ mSamplingKeys = samplingKeys;
+ }
+}
diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java
index 7b452a8..24cfc1b 100644
--- a/core/java/android/hardware/OverlayProperties.java
+++ b/core/java/android/hardware/OverlayProperties.java
@@ -50,6 +50,8 @@
// Invoked on destruction
private Runnable mCloser;
+ private LutProperties[] mLutProperties;
+
private OverlayProperties(long nativeObject) {
if (nativeObject != 0) {
mCloser = sRegistry.registerNativeAllocation(this, nativeObject);
@@ -70,6 +72,20 @@
}
/**
+ * Gets the lut properties of the display.
+ * @hide
+ */
+ public LutProperties[] getLutProperties() {
+ if (mNativeObject == 0) {
+ return null;
+ }
+ if (mLutProperties == null) {
+ mLutProperties = nGetLutProperties(mNativeObject);
+ }
+ return mLutProperties;
+ }
+
+ /**
* Indicates that hardware composition of a buffer encoded with the provided {@link DataSpace}
* and {@link HardwareBuffer.Format} is supported on the device.
*
@@ -140,4 +156,5 @@
long nativeObject, int dataspace, int format);
private static native void nWriteOverlayPropertiesToParcel(long nativeObject, Parcel dest);
private static native long nReadOverlayPropertiesFromParcel(Parcel in);
-}
+ private static native LutProperties[] nGetLutProperties(long nativeObject);
+}
\ No newline at end of file
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 2748e32..19e244a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -49,6 +49,7 @@
import android.gui.StalledTransactionInfo;
import android.gui.TrustedOverlay;
import android.hardware.DataSpace;
+import android.hardware.DisplayLuts;
import android.hardware.HardwareBuffer;
import android.hardware.OverlayProperties;
import android.hardware.SyncFence;
@@ -307,9 +308,9 @@
private static native StalledTransactionInfo nativeGetStalledTransactionInfo(int pid);
private static native void nativeSetDesiredPresentTimeNanos(long transactionObj,
long desiredPresentTimeNanos);
- private static native void nativeSetFrameTimeline(long transactionObj,
- long vsyncId);
private static native void nativeNotifyShutdown();
+ private static native void nativeSetLuts(long transactionObj, long nativeObject,
+ float[] buffers, int[] slots, int[] dimensions, int[] sizes, int[] samplingKeys);
/**
* Transforms that can be applied to buffers as they are displayed to a window.
@@ -4399,6 +4400,17 @@
return this;
}
+ /** @hide */
+ public @NonNull Transaction setLuts(@NonNull SurfaceControl sc,
+ @NonNull DisplayLuts displayLuts) {
+ checkPreconditions(sc);
+
+ nativeSetLuts(mNativeObject, sc.mNativeObject, displayLuts.getLutBuffers(),
+ displayLuts.getOffsets(), displayLuts.getLutDimensions(),
+ displayLuts.getLutSizes(), displayLuts.getLutSamplingKeys());
+ return this;
+ }
+
/**
* Sets the caching hint for the layer. By default, the caching hint is
* {@link CACHING_ENABLED}.
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index 96494b1..63de195 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "OverlayProperties"
// #define LOG_NDEBUG 0
+#include <android/gui/LutProperties.h>
#include <android/gui/OverlayProperties.h>
#include <binder/Parcel.h>
#include <gui/SurfaceComposerClient.h>
@@ -35,6 +36,12 @@
jclass clazz;
jmethodID ctor;
} gOverlayPropertiesClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gLutPropertiesClassInfo;
+
// ----------------------------------------------------------------------------
// OverlayProperties lifecycle
// ----------------------------------------------------------------------------
@@ -95,6 +102,36 @@
return reinterpret_cast<jlong>(overlayProperties);
}
+static jobjectArray android_hardware_OverlayProperties_getLutProperties(JNIEnv* env, jobject thiz,
+ jlong nativeObject) {
+ gui::OverlayProperties* overlayProperties =
+ reinterpret_cast<gui::OverlayProperties*>(nativeObject);
+ if (overlayProperties->lutProperties.has_value()) {
+ return NULL;
+ }
+ auto& lutProperties = overlayProperties->lutProperties.value();
+ if (lutProperties.empty()) {
+ return NULL;
+ }
+ int32_t size = static_cast<int32_t>(lutProperties.size());
+ jobjectArray nativeLutProperties =
+ env->NewObjectArray(size, gLutPropertiesClassInfo.clazz, NULL);
+ if (nativeLutProperties == NULL) {
+ return NULL;
+ }
+ for (int32_t i = 0; i < size; i++) {
+ if (lutProperties[i].has_value()) {
+ auto& item = lutProperties[i].value();
+ jobject properties =
+ env->NewObject(gLutPropertiesClassInfo.clazz, gLutPropertiesClassInfo.ctor,
+ static_cast<int32_t>(item.dimension), item.size,
+ item.samplingKeys.data());
+ env->SetObjectArrayElement(nativeLutProperties, i, properties);
+ }
+ }
+ return nativeLutProperties;
+}
+
// ----------------------------------------------------------------------------
// Serialization
// ----------------------------------------------------------------------------
@@ -161,6 +198,8 @@
{ "nReadOverlayPropertiesFromParcel", "(Landroid/os/Parcel;)J",
(void*) android_hardware_OverlayProperties_read },
{"nCreateDefault", "()J", (void*) android_hardware_OverlayProperties_createDefault },
+ {"nGetLutProperties", "(J)[Landroid/hardware/LutProperties;",
+ (void*) android_hardware_OverlayProperties_getLutProperties },
};
// clang-format on
@@ -171,5 +210,9 @@
gOverlayPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gOverlayPropertiesClassInfo.ctor =
GetMethodIDOrDie(env, gOverlayPropertiesClassInfo.clazz, "<init>", "(J)V");
+ clazz = FindClassOrDie(env, "android/hardware/LutProperties");
+ gLutPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+ gLutPropertiesClassInfo.ctor =
+ GetMethodIDOrDie(env, gLutPropertiesClassInfo.clazz, "<init>", "(IJ[I)V");
return err;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 71ba214..a939d92 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -33,11 +33,13 @@
#include <android_runtime/android_view_Surface.h>
#include <android_runtime/android_view_SurfaceControl.h>
#include <android_runtime/android_view_SurfaceSession.h>
+#include <cutils/ashmem.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include <private/gui/ComposerService.h>
#include <stdio.h>
@@ -736,6 +738,65 @@
transaction->setDesiredHdrHeadroom(ctrl, desiredRatio);
}
+static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jfloatArray jbufferArray, jintArray joffsetArray,
+ jintArray jdimensionArray, jintArray jsizeArray,
+ jintArray jsamplingKeyArray) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+
+ ScopedIntArrayRW joffsets(env, joffsetArray);
+ if (joffsets.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
+ return;
+ }
+ ScopedIntArrayRW jdimensions(env, jdimensionArray);
+ if (jdimensions.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
+ return;
+ }
+ ScopedIntArrayRW jsizes(env, jsizeArray);
+ if (jsizes.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
+ return;
+ }
+ ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
+ if (jsamplingKeys.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
+ return;
+ }
+
+ jsize numLuts = env->GetArrayLength(jdimensionArray);
+ std::vector<int32_t> offsets(joffsets.get(), joffsets.get() + numLuts);
+ std::vector<int32_t> dimensions(jdimensions.get(), jdimensions.get() + numLuts);
+ std::vector<int32_t> sizes(jsizes.get(), jsizes.get() + numLuts);
+ std::vector<int32_t> samplingKeys(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
+
+ ScopedFloatArrayRW jbuffers(env, jbufferArray);
+ if (jbuffers.get() == nullptr) {
+ jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
+ return;
+ }
+
+ // create the shared memory and copy jbuffers
+ size_t bufferSize = jbuffers.size() * sizeof(float);
+ int32_t fd = ashmem_create_region("lut_shread_mem", bufferSize);
+ if (fd < 0) {
+ jniThrowRuntimeException(env, "ashmem_create_region() failed");
+ return;
+ }
+ void* ptr = mmap(nullptr, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ jniThrowRuntimeException(env, "Failed to map the shared memory");
+ return;
+ }
+ memcpy(ptr, jbuffers.get(), bufferSize);
+ // unmap
+ munmap(ptr, bufferSize);
+
+ transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys);
+}
+
static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint cachingHint) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2541,6 +2602,7 @@
(void*) nativeSetDesiredPresentTimeNanos },
{"nativeNotifyShutdown", "()V",
(void*)nativeNotifyShutdown },
+ {"nativeSetLuts", "(JJ[F[I[I[I[I)V", (void*)nativeSetLuts },
// clang-format on
};
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
index 5876682..85dabce 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
@@ -30,29 +30,32 @@
*/
public class BubbleInfo implements Parcelable {
- private String mKey; // Same key as the Notification
+ private final String mKey; // Same key as the Notification
private int mFlags; // Flags from BubbleMetadata
@Nullable
- private String mShortcutId;
- private int mUserId;
- private String mPackageName;
+ private final String mShortcutId;
+ private final int mUserId;
+ private final String mPackageName;
/**
* All notification bubbles require a shortcut to be set on the notification, however, the
* app could still specify an Icon and PendingIntent to use for the bubble. In that case
* this icon will be populated. If the bubble is entirely shortcut based, this will be null.
*/
@Nullable
- private Icon mIcon;
+ private final Icon mIcon;
@Nullable
- private String mTitle;
+ private final String mTitle;
@Nullable
- private String mAppName;
- private boolean mIsImportantConversation;
- private boolean mShowAppBadge;
+ private final String mAppName;
+ private final boolean mIsImportantConversation;
+ private final boolean mShowAppBadge;
+ @Nullable
+ private final ParcelableFlyoutMessage mParcelableFlyoutMessage;
public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
int userId, String packageName, @Nullable String title, @Nullable String appName,
- boolean isImportantConversation, boolean showAppBadge) {
+ boolean isImportantConversation, boolean showAppBadge,
+ @Nullable ParcelableFlyoutMessage flyoutMessage) {
mKey = key;
mFlags = flags;
mShortcutId = shortcutId;
@@ -63,6 +66,7 @@
mAppName = appName;
mIsImportantConversation = isImportantConversation;
mShowAppBadge = showAppBadge;
+ mParcelableFlyoutMessage = flyoutMessage;
}
private BubbleInfo(Parcel source) {
@@ -76,6 +80,8 @@
mAppName = source.readString();
mIsImportantConversation = source.readBoolean();
mShowAppBadge = source.readBoolean();
+ mParcelableFlyoutMessage = source.readParcelable(
+ ParcelableFlyoutMessage.class.getClassLoader(), ParcelableFlyoutMessage.class);
}
public String getKey() {
@@ -122,6 +128,11 @@
return mShowAppBadge;
}
+ @Nullable
+ public ParcelableFlyoutMessage getParcelableFlyoutMessage() {
+ return mParcelableFlyoutMessage;
+ }
+
/**
* Whether this bubble is currently being hidden from the stack.
*/
@@ -180,6 +191,7 @@
parcel.writeString(mAppName);
parcel.writeBoolean(mIsImportantConversation);
parcel.writeBoolean(mShowAppBadge);
+ parcel.writeParcelable(mParcelableFlyoutMessage, flags);
}
@NonNull
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ParcelableFlyoutMessage.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ParcelableFlyoutMessage.kt
new file mode 100644
index 0000000..294d5e5
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ParcelableFlyoutMessage.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.wm.shell.shared.bubbles
+
+import android.graphics.drawable.Icon
+import android.os.Parcel
+import android.os.Parcelable
+
+/** The contents of the flyout message to be passed to launcher for rendering in the bubble bar. */
+class ParcelableFlyoutMessage(
+ val icon: Icon?,
+ val title: String?,
+ val message: String?,
+) : Parcelable {
+
+ constructor(
+ parcel: Parcel
+ ) : this(
+ icon = parcel.readParcelable(Icon::class.java.classLoader),
+ title = parcel.readString(),
+ message = parcel.readString(),
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeParcelable(icon, flags)
+ parcel.writeString(title)
+ parcel.writeString(message)
+ }
+
+ override fun describeContents() = 0
+
+ companion object {
+ @JvmField
+ val CREATOR =
+ object : Parcelable.Creator<ParcelableFlyoutMessage> {
+ override fun createFromParcel(parcel: Parcel) = ParcelableFlyoutMessage(parcel)
+
+ override fun newArray(size: Int) = arrayOfNulls<ParcelableFlyoutMessage>(size)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 169361a..e3fc5c2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -56,6 +56,7 @@
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.ParcelableFlyoutMessage;
import java.io.PrintWriter;
import java.util.List;
@@ -350,7 +351,22 @@
getTitle(),
getAppName(),
isImportantConversation(),
- !isAppLaunchIntent());
+ !isAppLaunchIntent(),
+ getParcelableFlyoutMessage());
+ }
+
+ /** Creates a parcelable flyout message to send to launcher. */
+ @Nullable
+ private ParcelableFlyoutMessage getParcelableFlyoutMessage() {
+ if (mFlyoutMessage == null) {
+ return null;
+ }
+ // the icon is only used in group chats
+ Icon icon = mFlyoutMessage.isGroupChat ? mFlyoutMessage.senderIcon : null;
+ String title =
+ mFlyoutMessage.senderName == null ? null : mFlyoutMessage.senderName.toString();
+ String message = mFlyoutMessage.message == null ? null : mFlyoutMessage.message.toString();
+ return new ParcelableFlyoutMessage(icon, title, message);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 3982a23..c5e3afd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -274,7 +274,7 @@
@Nullable BubbleExpandedView expandedView;
int dotColor;
Path dotPath;
- @Nullable Bubble.FlyoutMessage flyoutMessage;
+ Bubble.FlyoutMessage flyoutMessage;
Bitmap bubbleBitmap;
Bitmap badgeBitmap;
@@ -300,6 +300,10 @@
return null;
}
+ // set the flyout message but don't load the avatar because we can't pass it on the
+ // binder to launcher
+ info.flyoutMessage = b.getFlyoutMessage();
+
return info;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index 1b7bb0d..c12822a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -181,7 +181,7 @@
@Nullable BubbleExpandedView expandedView;
int dotColor;
Path dotPath;
- @Nullable Bubble.FlyoutMessage flyoutMessage;
+ Bubble.FlyoutMessage flyoutMessage;
Bitmap bubbleBitmap;
Bitmap badgeBitmap;
@@ -221,6 +221,10 @@
return null;
}
+ // set the flyout message but don't load the avatar because we can't pass it on the
+ // binder to launcher
+ info.flyoutMessage = b.getFlyoutMessage();
+
return info;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 7ba7034..79c31e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -356,6 +356,7 @@
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopTasksController> desktopTasksController,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
@@ -364,7 +365,8 @@
? shellInit
: null;
return new FreeformTaskListener(context, init, shellTaskOrganizer,
- desktopRepository, launchAdjacentController, windowDecorViewModel);
+ desktopRepository, desktopTasksController, launchAdjacentController,
+ windowDecorViewModel);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 4e548a6..5b9d2fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -110,6 +110,7 @@
import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
import com.android.wm.shell.windowdecor.extension.isFullscreen
import com.android.wm.shell.windowdecor.extension.isMultiWindow
+import com.android.wm.shell.windowdecor.extension.requestingImmersive
import java.io.PrintWriter
import java.util.Optional
import java.util.concurrent.Executor
@@ -1844,6 +1845,17 @@
userId = newUserId
}
+ /** Called when a task's info changes. */
+ fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
+ if (!Flags.enableFullyImmersiveInDesktop()) return
+ val inImmersive = taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)
+ val requestingImmersive = taskInfo.requestingImmersive
+ if (inImmersive && !requestingImmersive) {
+ // Exit immersive if the app is no longer requesting it.
+ exitDesktopTaskFromFullImmersive(taskInfo)
+ }
+ }
+
private fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopTasksController")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 73f7011..fbd3c10 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -30,6 +30,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
@@ -50,6 +51,7 @@
private final Context mContext;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<DesktopRepository> mDesktopRepository;
+ private final Optional<DesktopTasksController> mDesktopTasksController;
private final WindowDecorViewModel mWindowDecorationViewModel;
private final LaunchAdjacentController mLaunchAdjacentController;
@@ -65,12 +67,14 @@
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopTasksController> desktopTasksController,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorationViewModel) {
mContext = context;
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
mDesktopRepository = desktopRepository;
+ mDesktopTasksController = desktopTasksController;
mLaunchAdjacentController = launchAdjacentController;
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
@@ -147,6 +151,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
taskInfo.taskId);
+ mDesktopTasksController.ifPresent(c -> c.onTaskInfoChanged(taskInfo));
mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
state.mTaskInfo = taskInfo;
if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 9e5c1a6..ae4772e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -50,6 +50,7 @@
import android.view.DragEvent
import android.view.Gravity
import android.view.SurfaceControl
+import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_CLOSE
@@ -75,6 +76,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP
import com.android.wm.shell.MockToken
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -142,6 +144,7 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.times
@@ -3151,6 +3154,30 @@
})
}
+ @Test
+ @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() {
+ val task = setUpFreeformTask(DEFAULT_DISPLAY)
+ taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = true)
+
+ task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+ controller.onTaskInfoChanged(task)
+
+ verify(mockDesktopFullImmersiveTransitionHandler).exitImmersive(eq(task), any())
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun onTaskInfoChanged_notInImmersiveUnrequestsImmersive_noReExit() {
+ val task = setUpFreeformTask(DEFAULT_DISPLAY)
+ taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, immersive = false)
+
+ task.requestedVisibleTypes = WindowInsets.Type.statusBars()
+ controller.onTaskInfoChanged(task)
+
+ verify(mockDesktopFullImmersiveTransitionHandler, never()).exitImmersive(eq(task), any())
+ }
+
/**
* Assert that an unhandled drag event launches a PendingIntent with the
* windowing mode and bounds we are expecting.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 956ef14..36e0427 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -42,6 +42,7 @@
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -75,6 +76,8 @@
@Mock
private DesktopRepository mDesktopRepository;
@Mock
+ private DesktopTasksController mDesktopTasksController;
+ @Mock
private LaunchAdjacentController mLaunchAdjacentController;
private FreeformTaskListener mFreeformTaskListener;
private StaticMockitoSession mMockitoSession;
@@ -90,6 +93,7 @@
mShellInit,
mTaskOrganizer,
Optional.of(mDesktopRepository),
+ Optional.of(mDesktopTasksController),
mLaunchAdjacentController,
mWindowDecorViewModel);
}
@@ -177,6 +181,18 @@
verify(mDesktopRepository).removeFreeformTask(task.displayId, task.taskId);
}
+ @Test
+ public void onTaskInfoChanged_withDesktopController_forwards() {
+ ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ task.isVisible = true;
+ mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+ mFreeformTaskListener.onTaskInfoChanged(task);
+
+ verify(mDesktopTasksController).onTaskInfoChanged(task);
+ }
+
@After
public void tearDown() {
mMockitoSession.finishMocking();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
index 641063c..205defe 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.shared.bubbles
+import android.graphics.drawable.Icon
+import android.net.Uri
import android.os.Parcel
import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
import android.testing.AndroidTestingRunner
@@ -42,7 +44,12 @@
"title",
"Some app",
true,
- true
+ true,
+ ParcelableFlyoutMessage(
+ Icon.createWithContentUri(Uri.parse("content://image/123")),
+ "sender",
+ "message"
+ )
)
val parcel = Parcel.obtain()
bubbleInfo.writeToParcel(parcel, PARCELABLE_WRITE_RETURN_VALUE)
@@ -60,5 +67,10 @@
assertThat(bubbleInfo.appName).isEqualTo(bubbleInfoFromParcel.appName)
assertThat(bubbleInfo.isImportantConversation)
.isEqualTo(bubbleInfoFromParcel.isImportantConversation)
+ with(bubbleInfo.parcelableFlyoutMessage!!) {
+ assertThat(icon!!.uri.toString()).isEqualTo("content://image/123")
+ assertThat(title).isEqualTo("sender")
+ assertThat(message).isEqualTo("message")
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 4676dff..84a13ab 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -17,12 +17,14 @@
package android.media.tv.tuner.filter;
import android.annotation.BytesLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.media.AudioPresentation;
import android.media.MediaCodec.LinearBlock;
+import android.media.tv.flags.Flags;
import java.util.Collections;
import java.util.List;
@@ -57,12 +59,16 @@
private final int mScIndexMask;
private final AudioDescriptor mExtraMetaData;
private final List<AudioPresentation> mAudioPresentations;
+ private final int mNumDataPieces;
+ private final int mIndexInDataGroup;
+ private final int mDataGroupId;
// This constructor is used by JNI code only
private MediaEvent(int streamId, boolean isPtsPresent, long pts, boolean isDtsPresent, long dts,
long dataLength, long offset, LinearBlock buffer, boolean isSecureMemory, long dataId,
int mpuSequenceNumber, boolean isPrivateData, int scIndexMask,
- AudioDescriptor extraMetaData, List<AudioPresentation> audioPresentations) {
+ AudioDescriptor extraMetaData, List<AudioPresentation> audioPresentations,
+ int numDataPieces, int indexInDataGroup, int dataGroupId) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
@@ -78,6 +84,9 @@
mScIndexMask = scIndexMask;
mExtraMetaData = extraMetaData;
mAudioPresentations = audioPresentations;
+ mNumDataPieces = numDataPieces;
+ mIndexInDataGroup = indexInDataGroup;
+ mDataGroupId = dataGroupId;
}
/**
@@ -235,6 +244,67 @@
}
/**
+ * Gets the number of data pieces into which the original data was split.
+ *
+ * <p>The {@link #getNumDataPieces()}, {@link #getIndexInDataGroup()} and
+ * {@link #getDataGroupId()} methods should be used together to reassemble the original data if
+ * it was split into pieces. Use {@link #getLinearBlock()} to get the memory where the data
+ * pieces are stored.
+ *
+ * @return 0 or 1 if this MediaEvent object contains the complete data; otherwise the number of
+ * pieces into which the original data was split.
+ * @see #getIndexInDataGroup()
+ * @see #getDataGroupId()
+ * @see #getLinearBlock()
+ */
+ @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+ @IntRange(from = 0)
+ public int getNumDataPieces() {
+ return mNumDataPieces;
+ }
+
+ /**
+ * Gets the index of the data piece. The index in the data group indicates the order in which
+ * this {@link MediaEvent}'s data piece should be reassembled. The result should be within the
+ * range [0, {@link #getNumDataPieces()}).
+ *
+ * <p>The {@link #getNumDataPieces()}, {@link #getIndexInDataGroup()} and
+ * {@link #getDataGroupId()} methods should be used together to reassemble the original data if
+ * it was split into pieces. Use {@link #getLinearBlock()} to get the memory where the data
+ * pieces are stored.
+ *
+ * @return The index in the data group.
+ * @see #getNumDataPieces()
+ * @see #getDataGroupId()
+ * @see #getLinearBlock()
+ */
+ @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+ @IntRange(from = 0)
+ public int getIndexInDataGroup() {
+ return mIndexInDataGroup;
+ }
+
+ /**
+ * Gets the group ID for reassembling the complete data. {@link MediaEvent}s that have the same
+ * data group ID contain different pieces of the same data. This value should be ignored if
+ * {@link #getNumDataPieces()} returns 0 or 1.
+ *
+ * <p>The {@link #getNumDataPieces()}, {@link #getIndexInDataGroup()} and
+ * {@link #getDataGroupId()} methods should be used together to reassemble the original data if
+ * it was split into pieces. Use {@link #getLinearBlock()} to get the memory where the data
+ * pieces are stored.
+ *
+ * @return The data group ID.
+ * @see #getNumDataPieces()
+ * @see #getIndexInDataGroup()
+ * @see #getLinearBlock()
+ */
+ @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+ public int getDataGroupId() {
+ return mDataGroupId;
+ }
+
+ /**
* Finalize the MediaEvent object.
* @hide
*/
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 00b0e57..49e7941 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -686,12 +686,16 @@
} else if (mediaEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scVvc) {
sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
}
+ jint numDataPieces = mediaEvent.numDataPieces;
+ jint indexInDataGroup = mediaEvent.indexInDataGroup;
+ jint dataGroupId = mediaEvent.dataGroupId;
ScopedLocalRef obj(env, env->NewObject(mMediaEventClass, mMediaEventInitID, streamId,
isPtsPresent, pts, isDtsPresent, dts, dataLength,
offset, nullptr, isSecureMemory, avDataId,
mpuSequenceNumber, isPesPrivateData, sc,
- audioDescriptor.get(), presentationsJObj.get()));
+ audioDescriptor.get(), presentationsJObj.get(),
+ numDataPieces, indexInDataGroup, dataGroupId));
// Protect mFilterClient from being set to null.
android::Mutex::Autolock autoLock(mLock);
@@ -1048,7 +1052,7 @@
"<init>",
"(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
"ZJIZILandroid/media/tv/tuner/filter/AudioDescriptor;"
- "Ljava/util/List;)V");
+ "Ljava/util/List;III)V");
mAudioDescriptorInitID = env->GetMethodID(mAudioDescriptorClass, "<init>", "(BBCBBB)V");
mPesEventInitID = env->GetMethodID(mPesEventClass, "<init>", "(III)V");
mTsRecordEventInitID = env->GetMethodID(mTsRecordEventClass, "<init>", "(IIIJJI)V");
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 97d89a2..afa92f2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -83,11 +83,7 @@
}
val state = remember {
- MutableSceneTransitionLayoutState(
- currentScene,
- ClockTransition.defaultClockTransitions,
- enableInterruptions = false,
- )
+ MutableSceneTransitionLayoutState(currentScene, ClockTransition.defaultClockTransitions)
}
// Update state whenever currentSceneKey has changed.
@@ -102,7 +98,7 @@
scene(splitShadeLargeClockScene) {
LargeClockWithSmartSpace(
smartSpacePaddingTop = smartSpacePaddingTop,
- shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation
+ shouldOffSetClockToOneHalf = !hasCustomPositionUpdatedAnimation,
)
}
@@ -114,21 +110,15 @@
}
scene(smallClockScene) {
- SmallClockWithSmartSpace(
- smartSpacePaddingTop = smartSpacePaddingTop,
- )
+ SmallClockWithSmartSpace(smartSpacePaddingTop = smartSpacePaddingTop)
}
scene(largeClockScene) {
- LargeClockWithSmartSpace(
- smartSpacePaddingTop = smartSpacePaddingTop,
- )
+ LargeClockWithSmartSpace(smartSpacePaddingTop = smartSpacePaddingTop)
}
scene(WeatherClockScenes.largeClockScene) {
- WeatherLargeClockWithSmartSpace(
- smartSpacePaddingTop = smartSpacePaddingTop,
- )
+ WeatherLargeClockWithSmartSpace(smartSpacePaddingTop = smartSpacePaddingTop)
}
scene(WeatherClockScenes.splitShadeLargeClockScene) {
@@ -154,7 +144,7 @@
SmallClock(
burnInParams = burnIn.parameters,
onTopChanged = burnIn.onSmallClockTopChanged,
- modifier = Modifier.wrapContentSize()
+ modifier = Modifier.wrapContentSize(),
)
}
with(smartSpaceSection) {
@@ -202,7 +192,7 @@
y = 0,
)
}
- }
+ },
)
}
}
@@ -226,10 +216,7 @@
Column(modifier = modifier) {
val currentClock = currentClockState.value ?: return@Column
with(weatherClockSection) {
- Time(
- clock = currentClock,
- burnInParams = burnIn.parameters,
- )
+ Time(clock = currentClock, burnInParams = burnIn.parameters)
}
val density = LocalDensity.current
val context = LocalContext.current
@@ -242,7 +229,7 @@
modifier =
Modifier.heightIn(
min = getDimen(context, "enhanced_smartspace_height", density)
- )
+ ),
)
}
with(weatherClockSection) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 40b2a08..a0c56b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -139,7 +139,7 @@
TransitionStep(
KeyguardState.OFF,
KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED
+ transitionState = TransitionState.STARTED,
)
)
@@ -181,7 +181,7 @@
TransitionStep(
KeyguardState.AOD,
KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED
+ transitionState = TransitionState.STARTED,
)
)
@@ -206,7 +206,7 @@
TransitionStep(
KeyguardState.DOZING,
KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED
+ transitionState = TransitionState.STARTED,
)
)
@@ -229,7 +229,7 @@
TransitionStep(
KeyguardState.DOZING,
KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED
+ transitionState = TransitionState.STARTED,
)
)
@@ -244,12 +244,14 @@
fun faceAuthLockedOutStateIsUpdatedAfterUserSwitch() =
testScope.runTest {
underTest.start()
+ runCurrent()
+ fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
// User switching has started
fakeUserRepository.setSelectedUserInfo(primaryUser, SelectionStatus.SELECTION_COMPLETE)
fakeUserRepository.setSelectedUserInfo(
primaryUser,
- SelectionStatus.SELECTION_IN_PROGRESS
+ SelectionStatus.SELECTION_IN_PROGRESS,
)
runCurrent()
@@ -258,7 +260,7 @@
facePropertyRepository.setLockoutMode(secondaryUser.id, LockoutMode.NONE)
fakeUserRepository.setSelectedUserInfo(
secondaryUser,
- SelectionStatus.SELECTION_COMPLETE
+ SelectionStatus.SELECTION_COMPLETE,
)
runCurrent()
@@ -316,7 +318,7 @@
.isEqualTo(
Pair(
FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
- false
+ false,
)
)
}
@@ -600,7 +602,7 @@
faceAuthRepository.requestAuthenticate(
FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
- true
+ true,
)
facePropertyRepository.setCameraIno(CameraInfo("0", "1", null))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 17e3006..047d8c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -42,7 +42,8 @@
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testScope
@@ -57,7 +58,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito
import org.mockito.Mockito.reset
@ExperimentalCoroutinesApi
@@ -66,7 +66,7 @@
class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = Mockito.spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
private lateinit var underTest: FromAlternateBouncerTransitionInteractor
@@ -74,7 +74,7 @@
@Before
fun setup() {
- transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
underTest = kosmos.fromAlternateBouncerTransitionInteractor
underTest.start()
}
@@ -86,7 +86,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.OCCLUDED,
to = KeyguardState.ALTERNATE_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -111,7 +111,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.OCCLUDED,
to = KeyguardState.ALTERNATE_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -129,7 +129,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.OCCLUDED,
to = KeyguardState.ALTERNATE_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -158,7 +158,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.OCCLUDED,
to = KeyguardState.ALTERNATE_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -168,7 +168,7 @@
assertThat(transitionRepository)
.startedTransition(
from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.OCCLUDED
+ to = KeyguardState.OCCLUDED,
)
}
@@ -183,7 +183,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.ALTERNATE_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 33f3cd4..9300964 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -42,8 +42,9 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -69,7 +70,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -77,7 +77,7 @@
class FromAodTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
@@ -89,7 +89,7 @@
@Before
fun setup() {
powerInteractor = kosmos.powerInteractor
- transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
underTest = kosmos.fromAodTransitionInteractor
underTest.start()
@@ -101,7 +101,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.AOD,
- testScope
+ testScope,
)
kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.NONE)
reset(transitionRepository)
@@ -117,10 +117,7 @@
// Under default conditions, we should transition to LOCKSCREEN when waking up.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN)
}
@Test
@@ -133,10 +130,7 @@
// Waking with a SHOW_WHEN_LOCKED activity on top should transition to OCCLUDED.
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.AOD,
- to = KeyguardState.OCCLUDED,
- )
+ .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED)
}
@Test
@@ -363,13 +357,13 @@
from = KeyguardState.GONE,
to = KeyguardState.AOD,
transitionState = TransitionState.STARTED,
- value = 0f
+ value = 0f,
),
TransitionStep(
from = KeyguardState.GONE,
to = KeyguardState.AOD,
transitionState = TransitionState.RUNNING,
- value = 0.1f
+ value = 0.1f,
),
),
testScope = testScope,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index ff0a4a1..3b6e5d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -36,8 +36,9 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -79,7 +80,7 @@
class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
this.fakeCommunalSceneRepository =
spy(FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope))
}
@@ -105,7 +106,7 @@
@Before
fun setup() {
powerInteractor = kosmos.powerInteractor
- transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
underTest = kosmos.fromDozingTransitionInteractor
underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index fa304c9..9ca3ce6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -32,7 +32,9 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
@@ -53,7 +55,6 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -77,14 +78,21 @@
private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.fakeKeyguardTransitionRepository =
+ FakeKeyguardTransitionRepository(
+ // This test sends transition steps manually in the test cases.
+ sendTransitionStepsOnStartTransition = false,
+ testScope = testScope,
+ )
+
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
private val underTest by lazy { kosmos.fromDreamingTransitionInteractor }
private val powerInteractor = kosmos.powerInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
@Before
fun setup() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
index af76b08..57b1299 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -23,10 +23,10 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.AuthenticationFlags
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -41,18 +41,17 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
@SmallTest
@RunWith(AndroidJUnit4::class)
class FromGoneTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
private val underTest = kosmos.fromGoneTransitionInteractor
- private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
@Before
fun setUp() {
@@ -101,9 +100,7 @@
// We're in the middle of a GONE -> LOCKSCREEN transition.
assertThat(keyguardTransitionRepository)
- .startedTransition(
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(to = KeyguardState.LOCKSCREEN)
}
@Test
@@ -121,15 +118,13 @@
kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags(
AuthenticationFlags(
0,
- LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+ LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
)
)
runCurrent()
// We're in the middle of a GONE -> LOCKSCREEN transition.
assertThat(keyguardTransitionRepository)
- .startedTransition(
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(to = KeyguardState.LOCKSCREEN)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 4d81317..9c2e631 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -27,9 +27,11 @@
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -42,10 +44,10 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -53,15 +55,20 @@
class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
private val underTest = kosmos.fromLockscreenTransitionInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
private val shadeRepository = kosmos.fakeShadeRepository
private val keyguardRepository = kosmos.fakeKeyguardRepository
+ @Before
+ fun setup() {
+ transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
+ }
+
@Test
fun testSurfaceBehindVisibility() =
testScope.runTest {
@@ -256,4 +263,43 @@
assertThatRepository(transitionRepository)
.startedTransition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.DREAMING)
}
+
+ @Test
+ fun testTransitionsBackToOccluded_ifOccluded_andCanceledSwipe() =
+ testScope.runTest {
+ underTest.start()
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ keyguardRepository.setKeyguardDismissible(false)
+ keyguardRepository.setKeyguardOccluded(false)
+ runCurrent()
+
+ reset(transitionRepository)
+
+ shadeRepository.setLegacyShadeTracking(true)
+ runCurrent()
+ shadeRepository.setLegacyShadeExpansion(0.5f)
+ runCurrent()
+
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ )
+ reset(transitionRepository)
+
+ runCurrent()
+
+ shadeRepository.setLegacyShadeExpansion(0.6f)
+ shadeRepository.setLegacyShadeExpansion(0.7f)
+ runCurrent()
+
+ shadeRepository.setLegacyShadeExpansion(1f)
+ runCurrent()
+
+ assertThatRepository(transitionRepository)
+ .startedTransition(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.LOCKSCREEN,
+ )
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
index 7424320..4a90722 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -42,9 +42,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.testScope
@@ -60,7 +60,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -68,14 +67,14 @@
class FromOccludedTransitionInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
private val underTest = kosmos.fromOccludedTransitionInteractor
private val powerInteractor = kosmos.powerInteractor
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
@Before
fun setup() {
@@ -88,7 +87,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.OCCLUDED,
- testScope
+ testScope,
)
reset(transitionRepository)
}
@@ -102,10 +101,7 @@
runCurrent()
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
- )
+ .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.LOCKSCREEN)
}
@Test
@@ -122,9 +118,6 @@
runCurrent()
assertThat(transitionRepository)
- .startedTransition(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ .startedTransition(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 14f2d65..a7da230 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -26,9 +26,9 @@
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -44,20 +44,19 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class FromPrimaryBouncerTransitionInteractorTest : SysuiTestCase() {
- val kosmos =
+ private val kosmos =
testKosmos().apply {
- this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
val underTest = kosmos.fromPrimaryBouncerTransitionInteractor
val testScope = kosmos.testScope
val selectedUserInteractor = kosmos.selectedUserInteractor
- val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ val transitionRepository = kosmos.fakeKeyguardTransitionRepositorySpy
val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
@Test
@@ -67,12 +66,7 @@
runCurrent()
// Transition-specific surface visibility should be null ("don't care") initially.
- assertEquals(
- listOf(
- null,
- ),
- values
- )
+ assertEquals(listOf(null), values)
transitionRepository.sendTransitionStep(
TransitionStep(
@@ -86,9 +80,9 @@
assertEquals(
listOf(
- null, // PRIMARY_BOUNCER -> LOCKSCREEN does not have any specific visibility.
+ null // PRIMARY_BOUNCER -> LOCKSCREEN does not have any specific visibility.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -117,7 +111,7 @@
null,
false, // Surface is only made visible once the bouncer UI animates out.
),
- values
+ values,
)
transitionRepository.sendTransitionStep(
@@ -137,7 +131,7 @@
false,
true, // Surface should eventually be visible.
),
- values
+ values,
)
}
@@ -150,7 +144,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -161,7 +155,7 @@
assertThat(transitionRepository)
.startedTransition(
from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.LOCKSCREEN
+ to = KeyguardState.LOCKSCREEN,
)
}
@@ -177,7 +171,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -188,7 +182,7 @@
assertThat(transitionRepository)
.startedTransition(
from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GLANCEABLE_HUB
+ to = KeyguardState.GLANCEABLE_HUB,
)
}
@@ -201,7 +195,7 @@
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
- testScope
+ testScope,
)
reset(transitionRepository)
@@ -218,7 +212,7 @@
assertThat(transitionRepository)
.startedTransition(
from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.OCCLUDED
+ to = KeyguardState.OCCLUDED,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index a617484..8f3d549 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -41,9 +41,9 @@
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepositorySpy
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
@@ -76,7 +76,6 @@
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -91,7 +90,7 @@
class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
- fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
+ this.keyguardTransitionRepository = fakeKeyguardTransitionRepositorySpy
}
private val testScope = kosmos.testScope
@@ -99,7 +98,7 @@
private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
- private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepositorySpy }
private lateinit var featureFlags: FakeFeatureFlags
// Used to verify transition requests for test output
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.kt
new file mode 100644
index 0000000..22677b2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelTest.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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryBackgroundViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest: DeviceEntryBackgroundViewModel by lazy {
+ kosmos.deviceEntryBackgroundViewModel
+ }
+
+ @Test
+ fun lockscreenToDozingTransitionChangesBackgroundViewAlphaToZero() =
+ testScope.runTest {
+ kosmos.fingerprintPropertyRepository.supportsUdfps()
+ val alpha by collectLastValue(underTest.alpha)
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(dozingToLockscreen(0f, STARTED), dozingToLockscreen(0.1f)),
+ testScope,
+ )
+ runCurrent()
+ assertThat(alpha).isEqualTo(1.0f)
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(lockscreenToDozing(0f, STARTED)),
+ testScope,
+ )
+ runCurrent()
+
+ assertThat(alpha).isEqualTo(0.0f)
+ }
+
+ private fun lockscreenToDozing(value: Float, state: TransitionState = RUNNING): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ value = value,
+ transitionState = state,
+ ownerName = "DeviceEntryBackgroundViewModelTest",
+ )
+ }
+
+ private fun dozingToLockscreen(value: Float, state: TransitionState = RUNNING): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.DOZING,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = "DeviceEntryBackgroundViewModelTest",
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index 3e5dee6..a1edfc1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -53,7 +53,7 @@
private val bgExecutor = kosmos.fakeExecutor
private val userContextProvider: UserContextProvider = kosmos.userTracker
private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
- private lateinit var traceurMessageSender: TraceurMessageSender
+ private lateinit var traceurConnection: TraceurConnection
private val issueRecordingState =
IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
@@ -65,13 +65,13 @@
@Before
fun setup() {
- traceurMessageSender = mock<TraceurMessageSender>()
+ traceurConnection = mock<TraceurConnection>()
underTest =
IssueRecordingServiceSession(
bgExecutor,
dialogTransitionAnimator,
panelInteractor,
- traceurMessageSender,
+ traceurConnection,
issueRecordingState,
iActivityManager,
notificationManager,
@@ -85,7 +85,7 @@
bgExecutor.runAllReady()
Truth.assertThat(issueRecordingState.isRecording).isTrue()
- verify(traceurMessageSender).startTracing(any<TraceConfig>())
+ verify(traceurConnection).startTracing(any<TraceConfig>())
}
@Test
@@ -94,12 +94,12 @@
bgExecutor.runAllReady()
Truth.assertThat(issueRecordingState.isRecording).isFalse()
- verify(traceurMessageSender).stopTracing()
+ verify(traceurConnection).stopTracing()
}
@Test
fun cancelsNotification_afterReceivingShareCommand() {
- underTest.share(0, null, mContext)
+ underTest.share(0, null)
bgExecutor.runAllReady()
verify(notificationManager).cancelAsUser(isNull(), anyInt(), any<UserHandle>())
@@ -110,7 +110,7 @@
issueRecordingState.takeBugreport = true
val uri = mock<Uri>()
- underTest.share(0, uri, mContext)
+ underTest.share(0, uri)
bgExecutor.runAllReady()
verify(iActivityManager).requestBugReportWithExtraAttachment(uri)
@@ -121,17 +121,17 @@
issueRecordingState.takeBugreport = false
val uri = mock<Uri>()
- underTest.share(0, uri, mContext)
+ underTest.share(0, uri)
bgExecutor.runAllReady()
- verify(traceurMessageSender).shareTraces(mContext, uri)
+ verify(traceurConnection).shareTraces(uri)
}
@Test
fun closesShade_afterReceivingShareCommand() {
val uri = mock<Uri>()
- underTest.share(0, uri, mContext)
+ underTest.share(0, uri)
bgExecutor.runAllReady()
verify(panelInteractor).collapsePanels()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 8d84c3e..9639735 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -78,7 +78,6 @@
@Mock private lateinit var sysuiState: SysUiState
@Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
@Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock private lateinit var traceurMessageSender: TraceurMessageSender
private val systemClock = FakeSystemClock()
private val bgExecutor = FakeExecutor(systemClock)
private val mainExecutor = FakeExecutor(systemClock)
@@ -104,7 +103,7 @@
systemUIDialogManager,
sysuiState,
broadcastDispatcher,
- mDialogTransitionAnimator
+ mDialogTransitionAnimator,
)
)
@@ -120,7 +119,6 @@
mediaProjectionMetricsLogger,
screenCaptureDisabledDialogDelegate,
state,
- traceurMessageSender
) {
latch.countDown()
}
@@ -166,7 +164,7 @@
verify(mediaProjectionMetricsLogger, never())
.notifyProjectionInitiated(
anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
)
assertThat(screenRecordSwitch.isChecked).isFalse()
}
@@ -188,7 +186,7 @@
verify(mediaProjectionMetricsLogger)
.notifyProjectionInitiated(
anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
)
verify(factory, times(2)).create(any(SystemUIDialog.Delegate::class.java))
}
@@ -208,7 +206,7 @@
verify(mediaProjectionMetricsLogger)
.notifyProjectionInitiated(
anyInt(),
- eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER)
+ eq(SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER),
)
verify(factory, never()).create()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt
new file mode 100644
index 0000000..d90cca9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/TraceurConnectionTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.recordissue
+
+import android.os.IBinder
+import android.os.Looper
+import android.os.Messenger
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserContextProvider
+import com.android.traceur.PresetTraceConfigs
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class TraceurConnectionTest : SysuiTestCase() {
+
+ @Mock private lateinit var userContextProvider: UserContextProvider
+
+ private lateinit var underTest: TraceurConnection
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(userContextProvider.userContext).thenReturn(mContext)
+ underTest = TraceurConnection.Provider(userContextProvider, Looper.getMainLooper()).create()
+ }
+
+ @Test
+ fun onBoundRunnables_areRun_whenServiceIsBound() {
+ val latch = CountDownLatch(1)
+ underTest.onBound.add { latch.countDown() }
+
+ underTest.onServiceConnected(
+ InstrumentationRegistry.getInstrumentation().componentName,
+ mock(IBinder::class.java),
+ )
+
+ latch.await()
+ }
+
+ @Test
+ fun startTracing_sendsMsg_toStartTracing() {
+ underTest.binder = mock(Messenger::class.java)
+
+ underTest.startTracing(PresetTraceConfigs.getThermalConfig())
+
+ verify(underTest.binder)!!.send(any())
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/UserAwareConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/UserAwareConnectionTest.kt
new file mode 100644
index 0000000..f671bf4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/UserAwareConnectionTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.recordissue
+
+import android.content.Context
+import android.content.Intent
+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.settings.UserContextProvider
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class UserAwareConnectionTest : SysuiTestCase() {
+
+ @Mock private lateinit var userContextProvider: UserContextProvider
+ @Mock private lateinit var mockContext: Context
+
+ private lateinit var underTest: UserAwareConnection
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(userContextProvider.userContext).thenReturn(mockContext)
+ whenever(mockContext.bindService(any(), any(), anyInt())).thenReturn(true)
+ underTest = UserAwareConnection(userContextProvider, Intent())
+ }
+
+ @Test
+ fun doBindService_requestToBindToTheService_viaTheCorrectUserContext() {
+ underTest.doBind()
+
+ verify(userContextProvider).userContext
+ }
+
+ @Test
+ fun doBindService_DoesntRequestToBindToTheService_IfAlreadyRequested() {
+ underTest.doBind()
+ underTest.doBind()
+ underTest.doBind()
+
+ verify(userContextProvider, times(1)).userContext
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 3b5d5a8..b19b2d9 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -114,7 +114,7 @@
faceAuthenticationLogger.bouncerVisibilityChanged()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN,
- fallbackToDetect = false
+ fallbackToDetect = false,
)
}
.launchIn(applicationScope)
@@ -125,7 +125,7 @@
faceAuthenticationLogger.alternateBouncerVisibilityChanged()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN,
- fallbackToDetect = false
+ fallbackToDetect = false,
)
}
.launchIn(applicationScope)
@@ -153,7 +153,7 @@
it.lastWakeReason.powerManagerWakeReason
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_KEYGUARD_VISIBILITY_CHANGED,
- fallbackToDetect = true
+ fallbackToDetect = true,
)
}
.launchIn(applicationScope)
@@ -193,13 +193,16 @@
.map { (_, curr) -> curr.userInfo.id }
.sample(isBouncerVisible, ::Pair)
.onEach { (userId, isBouncerCurrentlyVisible) ->
+ if (!isFaceAuthEnabledAndEnrolled()) {
+ return@onEach
+ }
resetLockedOutState(userId)
yield()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
// Fallback to detection if bouncer is not showing so that we can detect a
// face and then show the bouncer to the user if face auth can't run
- fallbackToDetect = !isBouncerCurrentlyVisible
+ fallbackToDetect = !isBouncerCurrentlyVisible,
)
}
.launchIn(applicationScope)
@@ -210,7 +213,7 @@
repository.cancel()
runFaceAuth(
FaceAuthUiEvent.FACE_AUTH_CAMERA_AVAILABLE_CHANGED,
- fallbackToDetect = true
+ fallbackToDetect = true,
)
}
}
@@ -321,7 +324,7 @@
faceAuthenticationStatusOverride.value =
ErrorFaceAuthenticationStatus(
BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
- context.resources.getString(R.string.keyguard_face_unlock_unavailable)
+ context.resources.getString(R.string.keyguard_face_unlock_unavailable),
)
} else {
faceAuthenticationStatusOverride.value = null
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
index 0e2d9b6..43e39cf 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt
@@ -30,7 +30,7 @@
import java.time.Clock
import javax.inject.Inject
import kotlin.time.Duration
-import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.days
import kotlin.time.DurationUnit
import kotlin.time.toDuration
import kotlinx.coroutines.CoroutineScope
@@ -64,7 +64,7 @@
get() =
SystemProperties.getLong(
"persist.contextual_edu.initial_delay_sec",
- /* defaultValue= */ 72.hours.inWholeSeconds
+ /* defaultValue= */ 7.days.inWholeSeconds,
)
.toDuration(DurationUnit.SECONDS)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index ca1a800..68244d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -60,6 +60,7 @@
primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel,
primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
+ lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
) {
val color: Flow<Int> =
deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground ->
@@ -103,7 +104,9 @@
offToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
primaryBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
primaryBouncerToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
- primaryBouncerToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+ primaryBouncerToLockscreenTransitionViewModel
+ .deviceEntryBackgroundViewAlpha,
+ lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
)
.merge()
.onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index d3eefca..7abf35d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -55,16 +55,16 @@
onCancel = { 1f },
)
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+
override val deviceEntryParentViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
isUdfpsEnrolledAndEnabled ->
if (isUdfpsEnrolledAndEnabled) {
transitionAnimation.immediatelyTransitionTo(1f)
} else {
- transitionAnimation.sharedFlow(
- duration = 250.milliseconds,
- onStep = { 1f - it },
- )
+ transitionAnimation.sharedFlow(duration = 250.milliseconds, onStep = { 1f - it })
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index d89e73d..fb406d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -45,10 +45,11 @@
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
+import com.android.systemui.recordissue.IssueRecordingServiceConnection
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
-import com.android.systemui.recordissue.TraceurMessageSender
+import com.android.systemui.recordissue.TraceurConnection
import com.android.systemui.res.R
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.RecordingService
@@ -66,7 +67,7 @@
constructor(
host: QSHost,
uiEventLogger: QsEventLogger,
- @Background backgroundLooper: Looper,
+ @Background private val backgroundLooper: Looper,
@Main mainHandler: Handler,
falsingManager: FalsingManager,
metricsLogger: MetricsLogger,
@@ -78,7 +79,8 @@
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val panelInteractor: PanelInteractor,
private val userContextProvider: UserContextProvider,
- private val traceurMessageSender: TraceurMessageSender,
+ irsConnProvider: IssueRecordingServiceConnection.Provider,
+ traceurConnProvider: TraceurConnection.Provider,
@Background private val bgExecutor: Executor,
private val issueRecordingState: IssueRecordingState,
private val delegateFactory: RecordIssueDialogDelegate.Factory,
@@ -93,11 +95,20 @@
metricsLogger,
statusBarStateController,
activityStarter,
- qsLogger
+ qsLogger,
) {
private val onRecordingChangeListener = Runnable { refreshState() }
+ private val irsConnection: IssueRecordingServiceConnection = irsConnProvider.create()
+ private val traceurConnection =
+ traceurConnProvider.create().apply {
+ onBound.add {
+ getTags(issueRecordingState)
+ doUnBind()
+ }
+ }
+
override fun handleSetListening(listening: Boolean) {
super.handleSetListening(listening)
if (listening) {
@@ -109,7 +120,7 @@
override fun handleDestroy() {
super.handleDestroy()
- bgExecutor.execute { traceurMessageSender.unbindFromTraceur(mContext) }
+ bgExecutor.execute { irsConnection.doUnBind() }
}
override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label)
@@ -142,7 +153,7 @@
DELAY_MS,
INTERVAL_MS,
pendingServiceIntent(getStartIntent(userContextProvider.userContext)),
- pendingServiceIntent(getStopIntent(userContextProvider.userContext))
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext)),
)
private fun stopIssueRecordingService() =
@@ -154,10 +165,19 @@
userContextProvider.userContext,
RecordingService.REQUEST_CODE,
action,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
private fun showPrompt(expandable: Expandable?) {
+ bgExecutor.execute {
+ // We only want to get the tags once per session, as this is not likely to change, if at
+ // all on a month to month basis. Using onBound's size is a way to verify if the tag
+ // retrieval has already happened or not.
+ if (traceurConnection.onBound.isNotEmpty()) {
+ traceurConnection.doBind()
+ }
+ irsConnection.doBind()
+ }
val dialog: AlertDialog =
delegateFactory
.create {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 4d2bc91..3f875bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -23,9 +23,12 @@
import android.content.res.Resources
import android.net.Uri
import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
import android.util.Log
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.LongRunning
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
@@ -42,6 +45,7 @@
@Inject
constructor(
controller: RecordingController,
+ @Background private val bgLooper: Looper,
@LongRunning private val bgExecutor: Executor,
@Main handler: Handler,
uiEventLogger: UiEventLogger,
@@ -50,8 +54,8 @@
keyguardDismissUtil: KeyguardDismissUtil,
dialogTransitionAnimator: DialogTransitionAnimator,
panelInteractor: PanelInteractor,
- traceurMessageSender: TraceurMessageSender,
private val issueRecordingState: IssueRecordingState,
+ traceurConnectionProvider: TraceurConnection.Provider,
iActivityManager: IActivityManager,
) :
RecordingService(
@@ -64,18 +68,37 @@
keyguardDismissUtil,
) {
+ private val traceurConnection: TraceurConnection = traceurConnectionProvider.create()
+
private val session =
IssueRecordingServiceSession(
bgExecutor,
dialogTransitionAnimator,
panelInteractor,
- traceurMessageSender,
+ traceurConnection,
issueRecordingState,
iActivityManager,
notificationManager,
userContextProvider,
)
+ /**
+ * It is necessary to bind to IssueRecordingService from the Record Issue Tile because there are
+ * instances where this service is not created in the same user profile as the record issue tile
+ * aka, headless system user mode. In those instances, the TraceurConnection will be considered
+ * a leak in between notification actions unless the tile is bound to this service to keep it
+ * alive.
+ */
+ override fun onBind(intent: Intent): IBinder? {
+ traceurConnection.doBind()
+ return super.onBind(intent)
+ }
+
+ override fun onUnbind(intent: Intent?): Boolean {
+ traceurConnection.doUnBind()
+ return super.onUnbind(intent)
+ }
+
override fun getTag(): String = TAG
override fun getChannelId(): String = CHANNEL_ID
@@ -99,7 +122,6 @@
session.share(
intent.getIntExtra(EXTRA_NOTIFICATION_ID, mNotificationId),
intent.getParcelableExtra(EXTRA_PATH, Uri::class.java),
- this,
)
// Unlike all other actions, action_share has different behavior for the screen
// recording qs tile than it does for the record issue qs tile. Return sticky to
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt
new file mode 100644
index 0000000..85a5805
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceConnection.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.recordissue
+
+import android.content.Intent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserContextProvider
+import com.android.traceur.MessageConstants.SYSTEM_UI_PACKAGE_NAME
+import javax.inject.Inject
+
+/**
+ * It is necessary to bind to IssueRecordingService from the Record Issue Tile because there are
+ * instances where this service is not created in the same user profile as the record issue tile
+ * aka, headless system user mode. In those instances, the TraceurConnection will be considered a
+ * leak in between notification actions unless the tile is bound to this service to keep it alive.
+ */
+class IssueRecordingServiceConnection(userContextProvider: UserContextProvider) :
+ UserAwareConnection(
+ userContextProvider,
+ Intent().setClassName(SYSTEM_UI_PACKAGE_NAME, IssueRecordingService::class.java.name),
+ ) {
+ @SysUISingleton
+ class Provider @Inject constructor(private val userContextProvider: UserContextProvider) {
+ fun create() = IssueRecordingServiceConnection(userContextProvider)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
index e4d3e6c..ad9b4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
@@ -19,7 +19,6 @@
import android.app.IActivityManager
import android.app.NotificationManager
import android.content.ContentResolver
-import android.content.Context
import android.net.Uri
import android.os.UserHandle
import android.provider.Settings
@@ -42,7 +41,7 @@
private val bgExecutor: Executor,
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val panelInteractor: PanelInteractor,
- private val traceurMessageSender: TraceurMessageSender,
+ private val traceurConnection: TraceurConnection,
private val issueRecordingState: IssueRecordingState,
private val iActivityManager: IActivityManager,
private val notificationManager: NotificationManager,
@@ -50,7 +49,7 @@
) {
fun start() {
- bgExecutor.execute { traceurMessageSender.startTracing(issueRecordingState.traceConfig) }
+ bgExecutor.execute { traceurConnection.startTracing(issueRecordingState.traceConfig) }
issueRecordingState.isRecording = true
}
@@ -59,12 +58,12 @@
if (issueRecordingState.traceConfig.longTrace) {
Settings.Global.putInt(contentResolver, NOTIFY_SESSION_ENDED_SETTING, DISABLED)
}
- traceurMessageSender.stopTracing()
+ traceurConnection.stopTracing()
}
issueRecordingState.isRecording = false
}
- fun share(notificationId: Int, screenRecording: Uri?, context: Context) {
+ fun share(notificationId: Int, screenRecording: Uri?) {
bgExecutor.execute {
notificationManager.cancelAsUser(
null,
@@ -75,7 +74,7 @@
if (issueRecordingState.takeBugreport) {
iActivityManager.requestBugReportWithExtraAttachment(screenRecording)
} else {
- traceurMessageSender.shareTraces(context, screenRecording)
+ traceurConnection.shareTraces(screenRecording)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index ed67e64..6758c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -64,7 +64,6 @@
private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate,
private val state: IssueRecordingState,
- private val traceurMessageSender: TraceurMessageSender,
@Assisted private val onStarted: Runnable,
) : SystemUIDialog.Delegate {
@@ -87,10 +86,6 @@
setNegativeButton(R.string.cancel) { _, _ -> }
setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() }
}
- bgExecutor.execute {
- traceurMessageSender.onBoundToTraceur.add { traceurMessageSender.getTags(state) }
- traceurMessageSender.bindToTraceur(dialog.context)
- }
}
override fun createDialog(): SystemUIDialog = factory.create(this)
@@ -151,7 +146,7 @@
mediaProjectionMetricsLogger.notifyProjectionInitiated(
userTracker.userId,
- SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
+ SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER,
)
if (!state.hasUserApprovedScreenRecording) {
@@ -189,7 +184,7 @@
CustomTraceSettingsDialogDelegate(
factory,
state.customTraceState,
- state.tagTitles
+ state.tagTitles,
) {
onMenuItemClickListener.onMenuItemClick(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt
new file mode 100644
index 0000000..81529b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurConnection.kt
@@ -0,0 +1,152 @@
+/*
+ * 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.recordissue
+
+import android.content.ComponentName
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
+import android.util.Log
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
+import com.android.systemui.settings.UserContextProvider
+import com.android.traceur.FileSender
+import com.android.traceur.MessageConstants
+import com.android.traceur.MessageConstants.TRACING_APP_ACTIVITY
+import com.android.traceur.MessageConstants.TRACING_APP_PACKAGE_NAME
+import com.android.traceur.TraceConfig
+import java.util.concurrent.CopyOnWriteArrayList
+import javax.inject.Inject
+
+private const val TAG = "TraceurConnection"
+
+class TraceurConnection
+private constructor(userContextProvider: UserContextProvider, private val bgLooper: Looper) :
+ UserAwareConnection(
+ userContextProvider,
+ Intent().setClassName(TRACING_APP_PACKAGE_NAME, TRACING_APP_ACTIVITY),
+ ) {
+
+ @SysUISingleton
+ class Provider
+ @Inject
+ constructor(
+ private val userContextProvider: UserContextProvider,
+ @Background private val bgLooper: Looper,
+ ) {
+ fun create() = TraceurConnection(userContextProvider, bgLooper)
+ }
+
+ val onBound: MutableList<Runnable> = CopyOnWriteArrayList(mutableListOf())
+
+ override fun onServiceConnected(className: ComponentName, service: IBinder) {
+ super.onServiceConnected(className, service)
+ onBound.forEach(Runnable::run)
+ onBound.clear()
+ }
+
+ @WorkerThread
+ fun startTracing(traceType: TraceConfig) {
+ val data =
+ Bundle().apply { putParcelable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) }
+ sendMessage(MessageConstants.START_WHAT, data)
+ }
+
+ @WorkerThread fun stopTracing() = sendMessage(MessageConstants.STOP_WHAT)
+
+ @WorkerThread
+ fun shareTraces(screenRecord: Uri?) {
+ val replyHandler = Messenger(ShareFilesHandler(screenRecord, userContextProvider, bgLooper))
+ sendMessage(MessageConstants.SHARE_WHAT, replyTo = replyHandler)
+ }
+
+ @WorkerThread
+ fun getTags(state: IssueRecordingState) =
+ sendMessage(MessageConstants.TAGS_WHAT, replyTo = Messenger(TagsHandler(bgLooper, state)))
+
+ @WorkerThread
+ private fun sendMessage(what: Int, data: Bundle = Bundle(), replyTo: Messenger? = null) =
+ try {
+ val msg =
+ Message.obtain().apply {
+ this.what = what
+ this.data = data
+ this.replyTo = replyTo
+ }
+ binder?.send(msg) ?: onBound.add { binder!!.send(msg) }
+ } catch (e: Exception) {
+ Log.e(TAG, "failed to notify Traceur", e)
+ }
+}
+
+private class ShareFilesHandler(
+ private val screenRecord: Uri?,
+ private val userContextProvider: UserContextProvider,
+ looper: Looper,
+) : Handler(looper) {
+
+ override fun handleMessage(msg: Message) {
+ if (MessageConstants.SHARE_WHAT == msg.what) {
+ shareTraces(
+ msg.data.getParcelable(MessageConstants.EXTRA_PERFETTO, Uri::class.java),
+ msg.data.getParcelable(MessageConstants.EXTRA_WINSCOPE, Uri::class.java),
+ )
+ } else {
+ throw IllegalArgumentException("received unknown msg.what: " + msg.what)
+ }
+ }
+
+ private fun shareTraces(perfetto: Uri?, winscope: Uri?) {
+ val uris: ArrayList<Uri> =
+ ArrayList<Uri>().apply {
+ perfetto?.let { add(it) }
+ winscope?.let { add(it) }
+ screenRecord?.let { add(it) }
+ }
+ val fileSharingIntent =
+ FileSender.buildSendIntent(userContextProvider.userContext, uris)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
+ userContextProvider.userContext.startActivity(fileSharingIntent)
+ }
+}
+
+private class TagsHandler(looper: Looper, private val state: IssueRecordingState) :
+ Handler(looper) {
+
+ override fun handleMessage(msg: Message) {
+ if (MessageConstants.TAGS_WHAT == msg.what) {
+ val keys = msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAGS)
+ val values = msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAG_DESCRIPTIONS)
+ if (keys == null || values == null) {
+ throw IllegalArgumentException(
+ "Neither keys: $keys, nor values: $values can be null"
+ )
+ }
+ state.tagTitles =
+ keys.zip(values).map { it.first + TAG_TITLE_DELIMITER + it.second }.toSet()
+ } else {
+ throw IllegalArgumentException("received unknown msg.what: " + msg.what)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
deleted file mode 100644
index 8bfd14a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
+++ /dev/null
@@ -1,189 +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.
- */
-
-package com.android.systemui.recordissue
-
-import android.annotation.SuppressLint
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.ServiceConnection
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.os.Handler
-import android.os.IBinder
-import android.os.Looper
-import android.os.Message
-import android.os.Messenger
-import android.util.Log
-import androidx.annotation.WorkerThread
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.recordissue.IssueRecordingState.Companion.TAG_TITLE_DELIMITER
-import com.android.traceur.FileSender
-import com.android.traceur.MessageConstants
-import com.android.traceur.TraceConfig
-import javax.inject.Inject
-
-private const val TAG = "TraceurMessageSender"
-
-@SysUISingleton
-class TraceurMessageSender @Inject constructor(@Background private val backgroundLooper: Looper) {
- private var binder: Messenger? = null
- private var isBound: Boolean = false
-
- val onBoundToTraceur = mutableListOf<Runnable>()
-
- private val traceurConnection =
- object : ServiceConnection {
- override fun onServiceConnected(className: ComponentName, service: IBinder) {
- binder = Messenger(service)
- isBound = true
- onBoundToTraceur.forEach(Runnable::run)
- onBoundToTraceur.clear()
- }
-
- override fun onServiceDisconnected(className: ComponentName) {
- binder = null
- isBound = false
- }
- }
-
- @SuppressLint("WrongConstant")
- @WorkerThread
- fun bindToTraceur(context: Context) {
- if (isBound) {
- // Binding needs to happen after the phone has been unlocked. The RecordIssueTile is
- // initialized before this happens though, so binding is placed at a later time, during
- // normal operations that can be repeated. This check avoids calling "bindService" 2x+
- return
- }
- try {
- val info =
- context.packageManager.getPackageInfo(
- MessageConstants.TRACING_APP_PACKAGE_NAME,
- PackageManager.MATCH_SYSTEM_ONLY
- )
- val intent =
- Intent().setClassName(info.packageName, MessageConstants.TRACING_APP_ACTIVITY)
- val flags =
- Context.BIND_AUTO_CREATE or
- Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE or
- Context.BIND_WAIVE_PRIORITY
- context.bindService(intent, traceurConnection, flags)
- } catch (e: Exception) {
- Log.e(TAG, "failed to bind to Traceur's service", e)
- }
- }
-
- @WorkerThread
- fun unbindFromTraceur(context: Context) {
- if (isBound) {
- context.unbindService(traceurConnection)
- }
- }
-
- @WorkerThread
- fun startTracing(traceType: TraceConfig) {
- val data =
- Bundle().apply { putParcelable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) }
- notifyTraceur(MessageConstants.START_WHAT, data)
- }
-
- @WorkerThread fun stopTracing() = notifyTraceur(MessageConstants.STOP_WHAT)
-
- @WorkerThread
- fun shareTraces(context: Context, screenRecord: Uri?) {
- val replyHandler = Messenger(ShareFilesHandler(context, screenRecord, backgroundLooper))
- notifyTraceur(MessageConstants.SHARE_WHAT, replyTo = replyHandler)
- }
-
- @WorkerThread
- fun getTags(state: IssueRecordingState) {
- val replyHandler = Messenger(TagsHandler(backgroundLooper, state))
- notifyTraceur(MessageConstants.TAGS_WHAT, replyTo = replyHandler)
- }
-
- @WorkerThread
- private fun notifyTraceur(what: Int, data: Bundle = Bundle(), replyTo: Messenger? = null) {
- try {
- binder!!.send(
- Message.obtain().apply {
- this.what = what
- this.data = data
- this.replyTo = replyTo
- }
- )
- } catch (e: Exception) {
- Log.e(TAG, "failed to notify Traceur", e)
- }
- }
-
- private class ShareFilesHandler(
- private val context: Context,
- private val screenRecord: Uri?,
- looper: Looper,
- ) : Handler(looper) {
-
- override fun handleMessage(msg: Message) {
- if (MessageConstants.SHARE_WHAT == msg.what) {
- shareTraces(
- msg.data.getParcelable(MessageConstants.EXTRA_PERFETTO, Uri::class.java),
- msg.data.getParcelable(MessageConstants.EXTRA_WINSCOPE, Uri::class.java)
- )
- } else {
- throw IllegalArgumentException("received unknown msg.what: " + msg.what)
- }
- }
-
- private fun shareTraces(perfetto: Uri?, winscope: Uri?) {
- val uris: List<Uri> =
- mutableListOf<Uri>().apply {
- perfetto?.let { add(it) }
- winscope?.let { add(it) }
- screenRecord?.let { add(it) }
- }
- val fileSharingIntent =
- FileSender.buildSendIntent(context, uris)
- .addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
- )
- context.startActivity(fileSharingIntent)
- }
- }
-
- private class TagsHandler(looper: Looper, private val state: IssueRecordingState) :
- Handler(looper) {
-
- override fun handleMessage(msg: Message) {
- if (MessageConstants.TAGS_WHAT == msg.what) {
- val keys = msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAGS)
- val values =
- msg.data.getStringArrayList(MessageConstants.BUNDLE_KEY_TAG_DESCRIPTIONS)
- if (keys == null || values == null) {
- throw IllegalArgumentException(
- "Neither keys: $keys, nor values: $values can be null"
- )
- }
- state.tagTitles =
- keys.zip(values).map { it.first + TAG_TITLE_DELIMITER + it.second }.toSet()
- } else {
- throw IllegalArgumentException("received unknown msg.what: " + msg.what)
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
new file mode 100644
index 0000000..6aaa27d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.recordissue
+
+import android.annotation.SuppressLint
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.IBinder
+import android.os.Messenger
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import com.android.systemui.settings.UserContextProvider
+
+private const val TAG = "UserAwareConnection"
+private const val BIND_FLAGS =
+ Context.BIND_AUTO_CREATE or
+ Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE or
+ Context.BIND_WAIVE_PRIORITY
+
+/** ServiceConnection class that can be used to keep an IntentService alive. */
+open class UserAwareConnection(
+ protected val userContextProvider: UserContextProvider,
+ private val intent: Intent,
+) : ServiceConnection {
+ @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) var binder: Messenger? = null
+ private var shouldUnBind = false
+
+ override fun onServiceConnected(className: ComponentName, service: IBinder) {
+ binder = Messenger(service)
+ }
+
+ override fun onServiceDisconnected(className: ComponentName) {
+ binder = null
+ }
+
+ @SuppressLint("WrongConstant")
+ @WorkerThread
+ fun doBind() {
+ if (shouldUnBind) {
+ // Binding needs to happen after the phone has been unlocked. The RecordIssueTile is
+ // initialized before this happens though, so binding is placed at a later time, during
+ // normal operations that can be repeated. This check avoids calling "bindService" 2x+
+ return
+ }
+ try {
+ shouldUnBind = userContextProvider.userContext.bindService(intent, this, BIND_FLAGS)
+ } catch (e: Exception) {
+ Log.e(TAG, "failed to bind to the service", e)
+ }
+ }
+
+ @WorkerThread
+ fun doUnBind() {
+ if (shouldUnBind) {
+ userContextProvider.userContext.unbindService(this)
+ shouldUnBind = false
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index 0806be8..a762d84 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -97,12 +97,13 @@
private val window: ScreenshotWindow
private val actionExecutor: ActionExecutor
private val copyBroadcastReceiver: BroadcastReceiver
+ private val currentRequestCallbacks: MutableList<TakeScreenshotService.RequestCallback> =
+ mutableListOf()
private var screenshotSoundController: ScreenshotSoundController? = null
private var screenBitmap: Bitmap? = null
private var screenshotTakenInPortrait = false
private var screenshotAnimation: Animator? = null
- private var currentRequestCallback: TakeScreenshotService.RequestCallback? = null
private var packageName = ""
/** Tracks config changes that require re-creating UI */
@@ -169,8 +170,8 @@
requestCallback: TakeScreenshotService.RequestCallback,
) {
Assert.isMainThread()
+ screenshotHandler.resetTimeout()
- currentRequestCallback = requestCallback
if (screenshot.type == TAKE_SCREENSHOT_FULLSCREEN && screenshot.bitmap == null) {
val bounds = fullScreenRect
screenshot.bitmap = imageCapture.captureDisplay(display.displayId, bounds)
@@ -181,7 +182,7 @@
if (currentBitmap == null) {
Log.e(TAG, "handleScreenshot: Screenshot bitmap was null")
notificationController.notifyScreenshotError(R.string.screenshot_failed_to_capture_text)
- currentRequestCallback?.reportError()
+ requestCallback.reportError()
return
}
@@ -194,8 +195,10 @@
// User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
// and sharing shouldn't be exposed to the user.
saveScreenshotAndToast(screenshot, finisher)
+ requestCallback.onFinish()
return
}
+ currentRequestCallbacks.add(requestCallback)
broadcastSender.sendBroadcast(
Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
@@ -495,8 +498,8 @@
Log.d(TAG, "finishDismiss")
actionsController.endScreenshotSession()
scrollCaptureExecutor.close()
- currentRequestCallback?.onFinish()
- currentRequestCallback = null
+ currentRequestCallbacks.forEach { it.onFinish() }
+ currentRequestCallbacks.clear()
viewProxy.reset()
removeWindow()
screenshotHandler.cancelTimeout()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index b466bf0..b171e87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1160,11 +1160,13 @@
@Override
public void addHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mHeadsUpHeightChangedListeners.addIfAbsent(runnable);
}
@Override
public void removeHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mHeadsUpHeightChangedListeners.remove(runnable);
}
@@ -1240,11 +1242,13 @@
@Override
public void setScrolledToTop(boolean scrolledToTop) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mScrollViewFields.setScrolledToTop(scrolledToTop);
}
@Override
public void setStackTop(float stackTop) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
if (mAmbientState.getStackTop() != stackTop) {
mAmbientState.setStackTop(stackTop);
onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
@@ -1253,51 +1257,54 @@
@Override
public void setStackCutoff(float stackCutoff) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mAmbientState.setStackCutoff(stackCutoff);
}
@Override
public void setHeadsUpTop(float headsUpTop) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mAmbientState.setHeadsUpTop(headsUpTop);
requestChildrenUpdate();
}
@Override
public void setHeadsUpBottom(float headsUpBottom) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mAmbientState.setHeadsUpBottom(headsUpBottom);
mStateAnimator.setHeadsUpAppearHeightBottom(Math.round(headsUpBottom));
}
@Override
public void closeGutsOnSceneTouch() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mController.closeControlsDueToOutsideTouch();
}
@Override
public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mScrollViewFields.setSyntheticScrollConsumer(consumer);
}
@Override
public void setCurrentGestureOverscrollConsumer(@Nullable Consumer<Boolean> consumer) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mScrollViewFields.setCurrentGestureOverscrollConsumer(consumer);
}
@Override
public void setCurrentGestureInGutsConsumer(@Nullable Consumer<Boolean> consumer) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mScrollViewFields.setCurrentGestureInGutsConsumer(consumer);
}
@Override
public void setRemoteInputRowBottomBoundConsumer(@Nullable Consumer<Float> consumer) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
mScrollViewFields.setRemoteInputRowBottomBoundConsumer(consumer);
}
- @Override
- public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
- mScrollViewFields.setHeadsUpHeightConsumer(consumer);
- }
-
/**
* @param listener to be notified after the location of Notification children might have
* changed.
@@ -2621,11 +2628,13 @@
@Override
public int getTopHeadsUpHeight() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
return getTopHeadsUpIntrinsicHeight();
}
@Override
public int getHeadsUpInset() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
return mHeadsUpInset;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index c08ed61..f6e8b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -86,9 +86,6 @@
fun sendRemoteInputRowBottomBound(bottomY: Float?) =
remoteInputRowBottomBoundConsumer?.accept(bottomY)
- /** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
- fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
-
fun dump(pw: IndentingPrintWriter) {
pw.printSection("StackViewStates") {
pw.println("scrimClippingShape", scrimClippingShape)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index dbe81c1..6ad9f01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -77,9 +77,6 @@
/** Set a consumer for current remote input notification row bottom bound events */
fun setRemoteInputRowBottomBoundConsumer(consumer: Consumer<Float?>?)
- /** Set a consumer for heads up height changed events */
- fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
-
/** sets that scrolling is allowed */
fun setScrollingEnabled(enabled: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt
deleted file mode 100644
index 4e5ecfe..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone
-
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.policy.CallbackController
-import java.lang.ref.WeakReference
-import javax.inject.Inject
-
-/**
- * Publishes updates to the status bar's margins.
- *
- * While the status bar view consumes the entire width of the device, the status bar
- * contents are laid out with margins for rounded corners, padding from the absolute
- * edges, and potentially display cutouts in the corner.
- */
-@SysUISingleton
-class StatusBarLocationPublisher @Inject constructor()
-: CallbackController<StatusBarMarginUpdatedListener> {
- private val listeners = mutableSetOf<WeakReference<StatusBarMarginUpdatedListener>>()
-
- var marginLeft: Int = 0
- private set
- var marginRight: Int = 0
- private set
-
- override fun addCallback(listener: StatusBarMarginUpdatedListener) {
- listeners.add(WeakReference(listener))
- }
-
- override fun removeCallback(listener: StatusBarMarginUpdatedListener) {
- var toRemove: WeakReference<StatusBarMarginUpdatedListener>? = null
- for (l in listeners) {
- if (l.get() == listener) {
- toRemove = l
- }
- }
-
- if (toRemove != null) {
- listeners.remove(toRemove)
- }
- }
-
- fun updateStatusBarMargin(left: Int, right: Int) {
- marginLeft = left
- marginRight = right
-
- notifyListeners()
- }
-
- private fun notifyListeners() {
- var listenerList: List<WeakReference<StatusBarMarginUpdatedListener>>
- synchronized(this) {
- listenerList = listeners.toList()
- }
-
- listenerList.forEach { wrapper ->
- if (wrapper.get() == null) {
- listeners.remove(wrapper)
- }
-
- wrapper.get()?.onStatusBarMarginUpdated(marginLeft, marginRight)
- }
- }
-}
-
-interface StatusBarMarginUpdatedListener {
- fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int)
-}
\ No newline at end of file
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 a8b4728..c258095 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
@@ -65,7 +65,6 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarLocation;
-import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -140,7 +139,6 @@
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
- private final StatusBarLocationPublisher mLocationPublisher;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
private final CarrierConfigTracker mCarrierConfigTracker;
@@ -243,7 +241,6 @@
StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
- StatusBarLocationPublisher locationPublisher,
ShadeExpansionStateManager shadeExpansionStateManager,
StatusBarIconController statusBarIconController,
DarkIconManager.Factory darkIconManagerFactory,
@@ -267,7 +264,6 @@
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
- mLocationPublisher = locationPublisher;
mShadeExpansionStateManager = shadeExpansionStateManager;
mStatusBarIconController = statusBarIconController;
mCollapsedStatusBarViewModel = collapsedStatusBarViewModel;
@@ -349,9 +345,6 @@
}
mStatusBar = (PhoneStatusBarView) view;
- View contents = mStatusBar.findViewById(R.id.status_bar_contents);
- contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
- updateStatusBarLocation(contents.getLeft(), contents.getRight());
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
mStatusBar.restoreHierarchyState(
savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
@@ -977,13 +970,6 @@
}, /*isAnimationRunning*/ false);
}
- private void updateStatusBarLocation(int left, int right) {
- int leftMargin = left - mStatusBar.getLeft();
- int rightMargin = mStatusBar.getRight() - right;
-
- mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin);
- }
-
private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
@@ -991,14 +977,6 @@
}
};
- // Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
- private View.OnLayoutChangeListener mStatusBarLayoutListener =
- (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- if (left != oldLeft || right != oldRight) {
- updateStatusBarLocation(left, right);
- }
- };
-
@Override
public void dump(PrintWriter printWriter, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" ");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index ca518f9..c7da03d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles
import android.os.Handler
+import android.os.Looper
import android.service.quicksettings.Tile
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,9 +32,10 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.recordissue.IssueRecordingServiceConnection
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.recordissue.RecordIssueDialogDelegate
-import com.android.systemui.recordissue.TraceurMessageSender
+import com.android.systemui.recordissue.TraceurConnection
import com.android.systemui.res.R
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.settings.UserContextProvider
@@ -75,13 +77,14 @@
@Mock private lateinit var panelInteractor: PanelInteractor
@Mock private lateinit var userContextProvider: UserContextProvider
@Mock private lateinit var issueRecordingState: IssueRecordingState
- @Mock private lateinit var traceurMessageSender: TraceurMessageSender
@Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
@Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
@Mock private lateinit var dialog: SystemUIDialog
private lateinit var testableLooper: TestableLooper
private lateinit var tile: RecordIssueTile
+ private lateinit var irsConnProvider: IssueRecordingServiceConnection.Provider
+ private lateinit var traceurConnProvider: TraceurConnection.Provider
@Before
fun setUp() {
@@ -90,6 +93,10 @@
whenever(delegateFactory.create(any())).thenReturn(dialogDelegate)
whenever(dialogDelegate.createDialog()).thenReturn(dialog)
+ irsConnProvider = IssueRecordingServiceConnection.Provider(userContextProvider)
+ traceurConnProvider =
+ TraceurConnection.Provider(userContextProvider, Looper.getMainLooper())
+
testableLooper = TestableLooper.get(this)
tile =
RecordIssueTile(
@@ -107,7 +114,8 @@
dialogLauncherAnimator,
panelInteractor,
userContextProvider,
- traceurMessageSender,
+ irsConnProvider,
+ traceurConnProvider,
Executors.newSingleThreadExecutor(),
issueRecordingState,
delegateFactory,
@@ -169,7 +177,7 @@
.executeWhenUnlocked(
isA(ActivityStarter.OnDismissAction::class.java),
eq(false),
- eq(true)
+ eq(true),
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index eb1bcc7..d717fe4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -48,7 +48,7 @@
private val kosmos =
testKosmos().apply {
fakeKeyguardTransitionRepository =
- FakeKeyguardTransitionRepository(initInLockscreen = false)
+ FakeKeyguardTransitionRepository(initInLockscreen = false, testScope = testScope)
}
private val testScope = kosmos.testScope
private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
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 63a560f..e57e8d1 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
@@ -66,7 +66,6 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
-import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ui.DarkIconManager;
@@ -98,7 +97,6 @@
private ShadeExpansionStateManager mShadeExpansionStateManager;
private OngoingCallController mOngoingCallController;
private SystemStatusAnimationScheduler mAnimationScheduler;
- private StatusBarLocationPublisher mLocationPublisher;
// Set in instantiate()
private StatusBarIconController mStatusBarIconController;
private KeyguardStateController mKeyguardStateController;
@@ -1181,7 +1179,6 @@
setUpDaggerComponent();
mOngoingCallController = mock(OngoingCallController.class);
mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
- mLocationPublisher = mock(StatusBarLocationPublisher.class);
mStatusBarIconController = mock(StatusBarIconController.class);
mStatusBarStateController = mock(StatusBarStateController.class);
mKeyguardStateController = mock(KeyguardStateController.class);
@@ -1200,7 +1197,6 @@
mStatusBarFragmentComponentFactory,
mOngoingCallController,
mAnimationScheduler,
- mLocationPublisher,
mShadeExpansionStateManager,
mStatusBarIconController,
mIconManagerFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 4d0e603..70b4f79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -48,13 +48,35 @@
* with OFF -> GONE. Construct with initInLockscreen = false if your test requires this behavior.
*/
@SysUISingleton
-class FakeKeyguardTransitionRepository(private val initInLockscreen: Boolean = true) :
- KeyguardTransitionRepository {
+class FakeKeyguardTransitionRepository(
+ private val initInLockscreen: Boolean = true,
+
+ /**
+ * If true, calls to [startTransition] will automatically emit STARTED, RUNNING, and FINISHED
+ * transition steps from/to the given states.
+ *
+ * [startTransition] is what the From*TransitionInteractors call, so this more closely emulates
+ * the behavior of the real KeyguardTransitionRepository, and reduces the work needed to
+ * manually set up the repository state in each test. For example, setting dreaming=true will
+ * automatically cause FromDreamingTransitionInteractor to call startTransition(DREAMING), and
+ * then we'll send STARTED/RUNNING/FINISHED DREAMING TransitionSteps.
+ *
+ * If your test needs to make assertions at specific points between STARTED/FINISHED, or if it's
+ * difficult to set up all of the conditions to make the transition interactors actually call
+ * startTransition, then construct a FakeKeyguardTransitionRepository with this value false.
+ */
+ private val sendTransitionStepsOnStartTransition: Boolean = true,
+ private val testScope: TestScope,
+) : KeyguardTransitionRepository {
+
private val _transitions =
MutableSharedFlow<TransitionStep>(replay = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val transitions: SharedFlow<TransitionStep> = _transitions
- @Inject constructor() : this(initInLockscreen = true)
+ @Inject
+ constructor(
+ testScope: TestScope
+ ) : this(initInLockscreen = true, sendTransitionStepsOnStartTransition = true, testScope)
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
MutableStateFlow(
@@ -287,6 +309,11 @@
override suspend fun startTransition(info: TransitionInfo): UUID? {
_currentTransitionInfo.value = info
+
+ if (sendTransitionStepsOnStartTransition) {
+ sendTransitionSteps(from = info.from, to = info.to, testScope = testScope)
+ }
+
return if (info.animator == null) UUID.randomUUID() else null
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
index 3e69e87..e9eea83 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
@@ -18,9 +18,14 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import org.mockito.Mockito.spy
var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by
Kosmos.Fixture { fakeKeyguardTransitionRepository }
-var Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() }
+var Kosmos.fakeKeyguardTransitionRepository by
+ Kosmos.Fixture { FakeKeyguardTransitionRepository(testScope = testScope) }
+var Kosmos.fakeKeyguardTransitionRepositorySpy: FakeKeyguardTransitionRepository by
+ Kosmos.Fixture { spy(fakeKeyguardTransitionRepository) }
var Kosmos.realKeyguardTransitionRepository: KeyguardTransitionRepository by
Kosmos.Fixture { KeyguardTransitionRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index ef789d1..93a59eb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
@@ -27,7 +27,7 @@
val Kosmos.fromAodTransitionInteractor by
Kosmos.Fixture {
FromAodTransitionInteractor(
- transitionRepository = fakeKeyguardTransitionRepository,
+ transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index c694114..700d7e9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -18,8 +18,8 @@
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
@@ -29,7 +29,7 @@
val Kosmos.fromGoneTransitionInteractor by
Kosmos.Fixture {
FromGoneTransitionInteractor(
- transitionRepository = fakeKeyguardTransitionRepository,
+ transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
new file mode 100644
index 0000000..fc4f3a5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModelKosmos.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.deviceEntryBackgroundViewModel by Fixture {
+ DeviceEntryBackgroundViewModel(
+ context = applicationContext,
+ deviceEntryIconViewModel = deviceEntryIconViewModel,
+ configurationInteractor = configurationInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
+ alternateBouncerToDozingTransitionViewModel = alternateBouncerToDozingTransitionViewModel,
+ aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
+ dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
+ dreamingToAodTransitionViewModel = dreamingToAodTransitionViewModel,
+ dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+ goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+ goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
+ lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
+ occludedToAodTransitionViewModel = occludedToAodTransitionViewModel,
+ occludedToDozingTransitionViewModel = occludedToDozingTransitionViewModel,
+ occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
+ offToLockscreenTransitionViewModel = offToLockscreenTransitionViewModel,
+ primaryBouncerToAodTransitionViewModel = primaryBouncerToAodTransitionViewModel,
+ primaryBouncerToDozingTransitionViewModel = primaryBouncerToDozingTransitionViewModel,
+ primaryBouncerToLockscreenTransitionViewModel =
+ primaryBouncerToLockscreenTransitionViewModel,
+ lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
+ )
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0475b94..60dbf3f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -389,7 +389,8 @@
*/
private boolean shouldStartScoForUid(int uid) {
return !(UserHandle.isSameApp(uid, Process.BLUETOOTH_UID)
- || UserHandle.isSameApp(uid, Process.PHONE_UID));
+ || UserHandle.isSameApp(uid, Process.PHONE_UID)
+ || UserHandle.isSameApp(uid, Process.SYSTEM_UID));
}
@GuardedBy("mDeviceStateLock")
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 03fc60c..cd0a2a7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -106,7 +106,8 @@
protected final String TAG = getClass().getSimpleName().replace('$', '.');
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
+ protected static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
+ protected static final int ON_BINDING_DIED_REBIND_MSG = 1234;
protected static final String ENABLED_SERVICES_SEPARATOR = ":";
private static final String DB_VERSION_1 = "1";
private static final String DB_VERSION_2 = "2";
@@ -856,7 +857,13 @@
String approvedItem = getApprovedValue(pkgOrComponent);
if (approvedItem != null) {
+ final ComponentName component = ComponentName.unflattenFromString(approvedItem);
if (enabled) {
+ if (component != null && !isValidService(component, userId)) {
+ Log.e(TAG, "Skip allowing " + mConfig.caption + " " + pkgOrComponent
+ + " (userSet: " + userSet + ") for invalid service");
+ return;
+ }
approved.add(approvedItem);
} else {
approved.remove(approvedItem);
@@ -954,7 +961,7 @@
|| isPackageOrComponentAllowed(component.getPackageName(), userId))) {
return false;
}
- return componentHasBindPermission(component, userId);
+ return isValidService(component, userId);
}
private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1306,11 +1313,12 @@
if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
final ComponentName component = ComponentName.unflattenFromString(
approvedPackageOrComponent);
- if (component != null && !componentHasBindPermission(component, userId)) {
+ if (component != null && !isValidService(component, userId)) {
approved.removeAt(j);
if (DEBUG) {
Slog.v(TAG, "Removing " + approvedPackageOrComponent
- + " from approved list; no bind permission found "
+ + " from approved list; no bind permission or "
+ + "service interface filter found "
+ mConfig.bindPermission);
}
}
@@ -1329,6 +1337,11 @@
}
}
+ protected boolean isValidService(ComponentName component, int userId) {
+ return componentHasBindPermission(component, userId) && queryPackageForServices(
+ component.getPackageName(), userId).contains(component);
+ }
+
protected boolean isValidEntry(String packageOrComponent, int userId) {
return hasMatchingServices(packageOrComponent, userId);
}
@@ -1486,23 +1499,25 @@
* Called when user switched to unbind all services from other users.
*/
@VisibleForTesting
- void unbindOtherUserServices(int currentUser) {
+ void unbindOtherUserServices(int switchedToUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
- unbindServicesImpl(currentUser, true /* allExceptUser */);
+ t.traceBegin("ManagedServices.unbindOtherUserServices_current" + switchedToUser);
+ unbindServicesImpl(switchedToUser, true /* allExceptUser */);
t.traceEnd();
}
- void unbindUserServices(int user) {
+ void unbindUserServices(int removedUser) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("ManagedServices.unbindUserServices" + user);
- unbindServicesImpl(user, false /* allExceptUser */);
+ t.traceBegin("ManagedServices.unbindUserServices" + removedUser);
+ unbindServicesImpl(removedUser, false /* allExceptUser */);
t.traceEnd();
}
void unbindServicesImpl(int user, boolean allExceptUser) {
final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
synchronized (mMutex) {
+ // Remove enqueued rebinds to avoid rebinding services for a switched user
+ mHandler.removeMessages(ON_BINDING_DIED_REBIND_MSG);
final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
for (ManagedServiceInfo info : removableBoundServices) {
if ((allExceptUser && (info.userid != user))
@@ -1697,6 +1712,7 @@
mServicesRebinding.add(servicesBindingTag);
mHandler.postDelayed(() ->
reregisterService(name, userid),
+ ON_BINDING_DIED_REBIND_MSG,
ON_BINDING_DIED_REBIND_DELAY_MS);
} else {
Slog.v(TAG, getCaption() + " not rebinding in user " + userid
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index bd01351..c84711d 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION;
@@ -106,6 +107,16 @@
return isChangeEnabled(mActivityRecord, OVERRIDE_RESPECT_REQUESTED_ORIENTATION);
}
+ boolean shouldRespectRequestedOrientationDueToOverride() {
+ // Checking TaskFragment rather than ActivityRecord to ensure that transition
+ // between fullscreen and PiP would work well. Checking TaskFragment rather than
+ // Task to ensure that Activity Embedding is excluded.
+ return mActivityRecord.isVisibleRequested() && mActivityRecord.getTaskFragment() != null
+ && mActivityRecord.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+ .isOverrideRespectRequestedOrientationEnabled();
+ }
+
/**
* Whether an app is calling {@link android.app.Activity#setRequestedOrientation}
* in a loop and orientation request should be ignored.
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 0416f74..29ffda7 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -260,15 +259,14 @@
if (mDisplayContent == null) {
return false;
}
- ActivityRecord activity = mDisplayContent.topRunningActivity(
- /* considerKeyguardState= */ true);
- return activity != null && activity.getTaskFragment() != null
- // Checking TaskFragment rather than ActivityRecord to ensure that transition
- // between fullscreen and PiP would work well. Checking TaskFragment rather than
- // Task to ensure that Activity Embedding is excluded.
- && activity.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- && activity.mAppCompatController.getAppCompatOrientationOverrides()
- .isOverrideRespectRequestedOrientationEnabled();
+
+ // Top running activity can be freeform and ignore orientation request from bottom activity
+ // that should be respected, Check all activities in display to make sure any eligible
+ // activity should be respected.
+ final ActivityRecord activity = mDisplayContent.getActivity((r) ->
+ r.mAppCompatController.getAppCompatOrientationOverrides()
+ .shouldRespectRequestedOrientationDueToOverride());
+ return activity != null;
}
boolean getIgnoreOrientationRequest() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c062f5a..04a625b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -79,6 +79,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.window.flags.Flags.enableFullyImmersiveInDesktop;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -2515,10 +2516,16 @@
defaultTaskDisplayArea.getRootTask(task -> task.isVisible()
&& task.getTopLeafTask().getAdjacentTask() != null)
!= null;
- final boolean freeformRootTaskVisible =
- defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
+ final Task topFreeformTask = defaultTaskDisplayArea
+ .getTopRootTaskInWindowingMode(WINDOWING_MODE_FREEFORM);
+ final boolean freeformRootTaskVisible = topFreeformTask != null
+ && topFreeformTask.isVisible();
+ final boolean inNonFullscreenFreeformMode = freeformRootTaskVisible
+ && !topFreeformTask.getBounds().equals(mDisplayContent.getBounds());
- getInsetsPolicy().updateSystemBars(win, adjacentTasksVisible, freeformRootTaskVisible);
+ getInsetsPolicy().updateSystemBars(win, adjacentTasksVisible,
+ enableFullyImmersiveInDesktop()
+ ? inNonFullscreenFreeformMode : freeformRootTaskVisible);
final boolean topAppHidesStatusBar = topAppHidesSystemBar(Type.statusBars());
if (getStatusBar() != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 61b13a8..24a6f118 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -628,8 +628,9 @@
return (mForcedShowingTypes & types) == types;
}
- void updateSystemBars(WindowState win, boolean inSplitScreenMode, boolean inFreeformMode) {
- mForcedShowingTypes = (inSplitScreenMode || inFreeformMode)
+ void updateSystemBars(WindowState win, boolean inSplitScreenMode,
+ boolean inNonFullscreenFreeformMode) {
+ mForcedShowingTypes = (inSplitScreenMode || inNonFullscreenFreeformMode)
? (Type.statusBars() | Type.navigationBars())
: forceShowingNavigationBars(win)
? Type.navigationBars()
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 48bc9d7..3bbc6b2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -63,11 +63,13 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
+import android.testing.TestableLooper;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -82,6 +84,7 @@
import com.google.android.collect.Lists;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -103,6 +106,7 @@
import java.util.Set;
import java.util.concurrent.CountDownLatch;
+
public class ManagedServicesTest extends UiServiceTestCase {
@Mock
@@ -115,6 +119,7 @@
private ManagedServices.UserProfiles mUserProfiles;
@Mock private DevicePolicyManager mDpm;
Object mLock = new Object();
+ private TestableLooper mTestableLooper;
UserInfo mZero = new UserInfo(0, "zero", 0);
UserInfo mTen = new UserInfo(10, "ten", 0);
@@ -142,6 +147,7 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mTestableLooper = new TestableLooper(Looper.getMainLooper());
mContext.setMockPackageManager(mPm);
mContext.addMockSystemService(Context.USER_SERVICE, mUm);
@@ -199,6 +205,11 @@
mIpm, APPROVAL_BY_COMPONENT);
}
+ @After
+ public void tearDown() throws Exception {
+ mTestableLooper.destroy();
+ }
+
@Test
public void testBackupAndRestore_migration() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -888,7 +899,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
service.addApprovedList("a", 0, true);
service.reregisterService(cn, 0);
@@ -919,7 +930,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
service.addApprovedList("a", 0, false);
service.reregisterService(cn, 0);
@@ -950,7 +961,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
service.addApprovedList("a/a", 0, true);
service.reregisterService(cn, 0);
@@ -981,7 +992,7 @@
return true;
});
- mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
+ mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
service.addApprovedList("a/a", 0, false);
service.reregisterService(cn, 0);
@@ -1053,6 +1064,77 @@
}
@Test
+ public void registerService_bindingDied_rebindIsClearedOnUserSwitch() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mPkg);
+ when(context.getUserId()).thenReturn(mUser.getIdentifier());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_PACKAGE);
+ service = spy(service);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ // Trigger onBindingDied for component when registering
+ // => will schedule a rebind in 10 seconds
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onBindingDied(cn);
+ return true;
+ });
+ service.registerService(cn, 0);
+ assertThat(service.isBound(cn, 0)).isFalse();
+
+ // Switch to user 10
+ service.onUserSwitched(10);
+
+ // Check that the scheduled rebind for user 0 was cleared
+ mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
+ mTestableLooper.processAllMessages();
+ verify(service, never()).reregisterService(any(), anyInt());
+ }
+
+ @Test
+ public void registerService_bindingDied_rebindIsExecutedAfterTimeout() throws Exception {
+ Context context = mock(Context.class);
+ PackageManager pm = mock(PackageManager.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+ when(context.getPackageName()).thenReturn(mPkg);
+ when(context.getUserId()).thenReturn(mUser.getIdentifier());
+ when(context.getPackageManager()).thenReturn(pm);
+ when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+ APPROVAL_BY_PACKAGE);
+ service = spy(service);
+ ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+ // Trigger onBindingDied for component when registering
+ // => will schedule a rebind in 10 seconds
+ when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ ServiceConnection sc = (ServiceConnection) args[1];
+ sc.onBindingDied(cn);
+ return true;
+ });
+ service.registerService(cn, 0);
+ assertThat(service.isBound(cn, 0)).isFalse();
+
+ // Check that the scheduled rebind is run
+ mTestableLooper.moveTimeForward(ManagedServices.ON_BINDING_DIED_REBIND_DELAY_MS);
+ mTestableLooper.processAllMessages();
+ verify(service, times(1)).reregisterService(eq(cn), eq(0));
+ }
+
+ @Test
public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1211,6 +1293,64 @@
}
@Test
+ public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
+ Context context = spy(getContext());
+ doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
+
+ ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
+ mIpm, APPROVAL_BY_COMPONENT);
+
+ List<String> packages = new ArrayList<>();
+ packages.add("package");
+ addExpectedServices(service, packages, 0);
+
+ final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
+ final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
+
+ // Both components are approved initially
+ mExpectedPrimaryComponentNames.clear();
+ mExpectedPrimaryPackages.clear();
+ mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
+ mExpectedSecondaryComponentNames.clear();
+ mExpectedSecondaryPackages.clear();
+
+ loadXml(service);
+
+ //Component package/C1 loses serviceInterface intent filter
+ ManagedServices.Config config = service.getConfig();
+ when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenAnswer(new Answer<List<ResolveInfo>>() {
+ @Override
+ public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+ throws Throwable {
+ Object[] args = invocationOnMock.getArguments();
+ Intent invocationIntent = (Intent) args[0];
+ if (invocationIntent != null) {
+ if (invocationIntent.getAction().equals(config.serviceInterface)
+ && packages.contains(invocationIntent.getPackage())) {
+ List<ResolveInfo> dummyServices = new ArrayList<>();
+ ResolveInfo resolveInfo = new ResolveInfo();
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = invocationIntent.getPackage();
+ serviceInfo.name = approvedComponent.getClassName();
+ serviceInfo.permission = service.getConfig().bindPermission;
+ resolveInfo.serviceInfo = serviceInfo;
+ dummyServices.add(resolveInfo);
+ return dummyServices;
+ }
+ }
+ return new ArrayList<>();
+ }
+ });
+
+ // Trigger package update
+ service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
+
+ assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
+ assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
+ }
+
+ @Test
public void testSetPackageOrComponentEnabled() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1223,6 +1363,21 @@
"user10package1/K", "user10.3/Component", "user10package2/L",
"user10.4/Component"}));
+ // mock permissions for services
+ PackageManager pm = mock(PackageManager.class);
+ when(getContext().getPackageManager()).thenReturn(pm);
+ List<ComponentName> enabledComponents = List.of(
+ ComponentName.unflattenFromString("package/Comp"),
+ ComponentName.unflattenFromString("package/C2"),
+ ComponentName.unflattenFromString("again/M4"),
+ ComponentName.unflattenFromString("user10package/B"),
+ ComponentName.unflattenFromString("user10/Component"),
+ ComponentName.unflattenFromString("user10package1/K"),
+ ComponentName.unflattenFromString("user10.3/Component"),
+ ComponentName.unflattenFromString("user10package2/L"),
+ ComponentName.unflattenFromString("user10.4/Component"));
+ mockServiceInfoWithMetaData(enabledComponents, service, pm, new ArrayMap<>());
+
for (int userId : expectedEnabled.keySet()) {
ArrayList<String> expectedForUser = expectedEnabled.get(userId);
for (int i = 0; i < expectedForUser.size(); i++) {
@@ -1944,7 +2099,7 @@
metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
metaDatas.put(cn_allowed, metaDataAutobindAllow);
- mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
service.addApprovedList(cn_allowed.flattenToString(), 0, true);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -1989,7 +2144,7 @@
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2028,7 +2183,7 @@
metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
- mockServiceInfoWithMetaData(componentNames, service, metaDatas);
+ mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2099,8 +2254,8 @@
}
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
- ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
- throws RemoteException {
+ ManagedServices service, PackageManager packageManager,
+ ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
(Answer<ServiceInfo>) invocation -> {
ComponentName invocationCn = invocation.getArgument(0);
@@ -2115,6 +2270,39 @@
return null;
}
);
+
+ // add components to queryIntentServicesAsUser response
+ final List<String> packages = new ArrayList<>();
+ for (ComponentName cn: componentNames) {
+ packages.add(cn.getPackageName());
+ }
+ ManagedServices.Config config = service.getConfig();
+ when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
+ thenAnswer(new Answer<List<ResolveInfo>>() {
+ @Override
+ public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
+ throws Throwable {
+ Object[] args = invocationOnMock.getArguments();
+ Intent invocationIntent = (Intent) args[0];
+ if (invocationIntent != null) {
+ if (invocationIntent.getAction().equals(config.serviceInterface)
+ && packages.contains(invocationIntent.getPackage())) {
+ List<ResolveInfo> dummyServices = new ArrayList<>();
+ for (ComponentName cn: componentNames) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = invocationIntent.getPackage();
+ serviceInfo.name = cn.getClassName();
+ serviceInfo.permission = service.getConfig().bindPermission;
+ resolveInfo.serviceInfo = serviceInfo;
+ dummyServices.add(resolveInfo);
+ }
+ return dummyServices;
+ }
+ }
+ return new ArrayList<>();
+ }
+ });
}
private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 797b95b5..7e4ae67 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -25,6 +25,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
+
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
@@ -193,6 +194,8 @@
public void testWriteXml_userTurnedOffNAS() throws Exception {
int userId = ActivityManager.getCurrentUser();
+ doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
+
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
@@ -398,6 +401,10 @@
public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
+
+ doReturn(true).when(mAssistants).isValidService(eq(component1), eq(mZero.id));
+ doReturn(true).when(mAssistants).isValidService(eq(component2), eq(mZero.id));
+
mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true,
true, true);
verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal(
@@ -543,6 +550,7 @@
public void testSetAdjustmentTypeSupportedState() throws Exception {
int userId = ActivityManager.getCurrentUser();
+ doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -566,6 +574,7 @@
public void testSetAdjustmentTypeSupportedState_readWriteXml_entries() throws Exception {
int userId = ActivityManager.getCurrentUser();
+ doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
@@ -589,6 +598,7 @@
public void testSetAdjustmentTypeSupportedState_readWriteXml_empty() throws Exception {
int userId = ActivityManager.getCurrentUser();
+ doReturn(true).when(mAssistants).isValidService(eq(mCn), eq(userId));
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true,
true, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 03cad24..592eec5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2291,10 +2291,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
NotificationRecord r = getBeepyNotification();
@@ -2338,10 +2335,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
NotificationRecord r = getBeepyNotification();
@@ -2379,10 +2373,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
NotificationRecord r = getBeepyNotification();
@@ -2428,10 +2419,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
NotificationRecord r = getBeepyNotification();
r.getNotification().category = Notification.CATEGORY_EVENT;
@@ -2504,10 +2492,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
// Regular notification: should beep at 0% volume
NotificationRecord r = getBeepyNotification();
@@ -2574,10 +2559,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
NotificationRecord r = getBeepyNotification();
@@ -2602,10 +2584,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
// CATEGORY_ALARM is exempted
NotificationRecord r = getBeepyNotification();
@@ -2646,10 +2625,7 @@
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
- // Trigger avalanche trigger intent
- final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ triggerAvalancheEvent();
// Create a conversation group with GROUP_ALERT_SUMMARY behavior
// Where the summary is not MessagingStyle
@@ -2693,6 +2669,16 @@
assertEquals(-1, summary.getLastAudiblyAlertedMs());
}
+ private void triggerAvalancheEvent() throws Exception {
+ // Trigger avalanche trigger intent
+ final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", false);
+ mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+ // Wait after avalanche trigger before posting notifications
+ // so that notification#getWhen() is not the same value
+ Thread.sleep(100);
+ }
+
@Test
public void testBeepVolume_politeNotif_exemptEmergency() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
index d9b5f37..8747cfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -17,11 +17,13 @@
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
import static android.content.pm.ActivityInfo.OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
import static com.android.server.wm.AppCompatOrientationOverrides.OrientationOverridesState.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
@@ -31,6 +33,7 @@
import static org.junit.Assert.assertTrue;
import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ActivityInfo.ScreenOrientation;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
@@ -228,6 +231,25 @@
});
}
+ @Test
+ public void testOverrideRespectRequestedOrientationIsEnabled_bottomOrientationIsRespected() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.setIgnoreOrientationRequest(true);
+ a.createActivityWithComponentInNewTask();
+ robot.setOverrideRespectRequestedOrientationEnabled(true);
+ a.configureUnresizableTopActivity(SCREEN_ORIENTATION_LANDSCAPE);
+ robot.checkDisplayShouldIgnoreOrientationRequest(SCREEN_ORIENTATION_LANDSCAPE,
+ /* expected */ false);
+
+ a.createActivityWithComponentInNewTask();
+ a.setTopActivityInFreeformWindowingMode(true);
+ });
+ robot.checkDisplayShouldIgnoreOrientationRequest(SCREEN_ORIENTATION_LANDSCAPE,
+ /* expected */ false);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -291,6 +313,22 @@
}
}
+ void setOverrideRespectRequestedOrientationEnabled(boolean override) {
+ spyOn(getTopOrientationOverrides());
+ doReturn(override).when(getTopOrientationOverrides())
+ .isOverrideRespectRequestedOrientationEnabled();
+ }
+
+ void checkDisplayShouldIgnoreOrientationRequest(@ScreenOrientation int candidate,
+ boolean expected) {
+ assertEquals(expected, activity().displayContent()
+ .shouldIgnoreOrientationRequest(candidate));
+ }
+
+ void checkExpectedDisplayOrientation(@ScreenOrientation int expected) {
+ assertEquals(expected, activity().displayContent().getOrientation());
+ }
+
void checkShouldUseDisplayLandscapeNaturalOrientation(boolean expected) {
assertEquals(expected,
getTopOrientationOverrides().shouldUseDisplayLandscapeNaturalOrientation());
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index b26c267..d2cf03d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -41,7 +41,10 @@
import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
+import android.graphics.Rect;
import android.os.Binder;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
@@ -52,6 +55,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
@@ -95,6 +99,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
public void testControlsForDispatch_freeformTaskVisible() {
addStatusBar();
addNavigationBar();
@@ -108,6 +113,37 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void testControlsForDispatch_fullscreenFreeformTaskVisible() {
+ addStatusBar();
+ addNavigationBar();
+
+ final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+ ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ win.setBounds(new Rect());
+ final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+ // The freeform (w/fullscreen bounds) app window can control both system bars.
+ assertNotNull(controls);
+ assertEquals(2, controls.length);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void testControlsForDispatch_nonFullscreenFreeformTaskVisible() {
+ addStatusBar();
+ addNavigationBar();
+
+ final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+ ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ win.getTask().setBounds(new Rect(1, 1, 10, 10));
+ final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+ // The freeform (but not fullscreen bounds) app window must not control any system bars.
+ assertNull(controls);
+ }
+
+ @Test
public void testControlsForDispatch_forceStatusBarVisible() {
addStatusBar().mAttrs.forciblyShownTypes |= statusBars();
addNavigationBar();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ff966ae..f01cfc1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2741,7 +2741,7 @@
/**
* Returns a constant indicating the device phone type. This
- * indicates the type of radio used to transmit voice calls.
+ * indicates the type of radio used to transmit voice/data calls.
*
* @see #PHONE_TYPE_NONE
* @see #PHONE_TYPE_GSM
@@ -2753,7 +2753,7 @@
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY)
public int getPhoneType() {
- if (!isVoiceCapable()) {
+ if (!isVoiceCapable() && !isDataCapable()) {
return PHONE_TYPE_NONE;
}
return getCurrentPhoneType();