Merge "Migrate FontScalingTile" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 3644450..cf5a261 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6355,6 +6355,7 @@
field public static final String CATEGORY_STOPWATCH = "stopwatch";
field public static final String CATEGORY_SYSTEM = "sys";
field public static final String CATEGORY_TRANSPORT = "transport";
+ field @FlaggedApi("android.app.category_voicemail") public static final String CATEGORY_VOICEMAIL = "voicemail";
field public static final String CATEGORY_WORKOUT = "workout";
field @ColorInt public static final int COLOR_DEFAULT = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification> CREATOR;
@@ -21193,6 +21194,7 @@
method public int getStreamMinVolume(int);
method public int getStreamVolume(int);
method public float getStreamVolumeDb(int, int, int);
+ method @FlaggedApi("android.media.audio.supported_device_types_api") @NonNull public java.util.Set<java.lang.Integer> getSupportedDeviceTypes(int);
method @NonNull public java.util.List<android.media.AudioMixerAttributes> getSupportedMixerAttributes(@NonNull android.media.AudioDeviceInfo);
method @Deprecated public int getVibrateSetting(int);
method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3b18dac..86f2b628 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3209,6 +3209,11 @@
package android.companion.virtual {
+ public final class VirtualDevice implements android.os.Parcelable {
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomAudioInputSupport();
+ method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomCameraSupport();
+ }
+
public final class VirtualDeviceManager {
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
method @FlaggedApi("android.companion.virtual.flags.persistent_device_id_api") @NonNull public java.util.Set<java.lang.String> getAllPersistentDeviceIds();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a81ad3c..d705eeb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1061,6 +1061,12 @@
public static final String CATEGORY_MISSED_CALL = "missed_call";
/**
+ * Notification category: voicemail.
+ */
+ @FlaggedApi(Flags.FLAG_CATEGORY_VOICEMAIL)
+ public static final String CATEGORY_VOICEMAIL = "voicemail";
+
+ /**
* One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
* that best describes this Notification. May be used by the system for ranking and filtering.
*/
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b21b0f3..ba9c895 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1618,6 +1618,19 @@
return new ContactKeysManager(ctx);
}});
+ // DO NOT do a flag check like this unless the flag is read-only.
+ // (because this code is executed during preload in zygote.)
+ // If the flag is mutable, the check should be inside CachedServiceFetcher.
+ if (Flags.bicClient()) {
+ registerService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE,
+ BackgroundInstallControlManager.class,
+ new CachedServiceFetcher<BackgroundInstallControlManager>() {
+ @Override
+ public BackgroundInstallControlManager createService(ContextImpl ctx) {
+ return new BackgroundInstallControlManager(ctx);
+ }
+ });
+ }
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index d11c6c5..a5d4a14 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -43,3 +43,10 @@
description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
bug: "309920145"
}
+
+flag {
+ name: "category_voicemail"
+ namespace: "wear_sysui"
+ description: "Adds a new voicemail category for notifications"
+ bug: "322806700"
+}
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index d0c8be6..97fa2ba 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -17,11 +17,14 @@
package android.companion.virtual;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.companion.virtual.flags.Flags;
import android.content.Context;
import android.os.Parcel;
@@ -164,6 +167,44 @@
}
}
+ /**
+ * Returns whether this device may have custom audio input device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS)
+ public boolean hasCustomAudioInputSupport() {
+ try {
+ return mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO) == DEVICE_POLICY_CUSTOM;
+ // TODO(b/291735254): also check for a custom audio injection mix for this device id.
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether this device may have custom cameras.
+ *
+ * <p>Returning {@code true} does not necessarily mean that this device has cameras, it only
+ * means that a {@link android.hardware.camera2.CameraManager} instance created from a
+ * {@link Context} associated with this device will return this device's cameras, if any.</p>
+ *
+ * @see Context#getDeviceId()
+ * @see Context#createDeviceContext(int)
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_VDM_PUBLIC_APIS)
+ public boolean hasCustomCameraSupport() {
+ try {
+ return mVirtualDevice.getDevicePolicy(POLICY_TYPE_CAMERA) == DEVICE_POLICY_CUSTOM;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index bde562d..33b1134 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -409,6 +409,7 @@
"packageName = " + mAttributionSourceState.packageName + ", " +
"attributionTag = " + mAttributionSourceState.attributionTag + ", " +
"token = " + mAttributionSourceState.token + ", " +
+ "deviceId = " + mAttributionSourceState.deviceId + ", " +
"next = " + (mAttributionSourceState.next != null
&& mAttributionSourceState.next.length > 0
? mAttributionSourceState.next[0] : null) +
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 3413ede..969e47b 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -25,6 +25,7 @@
#include <android_os_Parcel.h>
#include <audiomanager/AudioManager.h>
#include <jni.h>
+#include <media/AidlConversion.h>
#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
@@ -66,6 +67,11 @@
jmethodID toArray;
} gArrayListMethods;
+static jclass gIntArrayClass;
+static struct {
+ jmethodID add;
+} gIntArrayMethods;
+
static jclass gBooleanClass;
static jmethodID gBooleanCstor;
@@ -1607,6 +1613,48 @@
return jStatus;
}
+// From AudioDeviceInfo
+static const int GET_DEVICES_INPUTS = 0x0001;
+static const int GET_DEVICES_OUTPUTS = 0x0002;
+
+static int android_media_AudioSystem_getSupportedDeviceTypes(JNIEnv *env, jobject clazz,
+ jint direction, jobject jDeviceTypes) {
+ if (jDeviceTypes == NULL) {
+ ALOGE("%s NULL Device Types IntArray", __func__);
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jDeviceTypes, gIntArrayClass)) {
+ ALOGE("%s not an IntArray", __func__);
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+
+ // Convert AudioManager.GET_DEVICES_ flags to AUDIO_PORT_ROLE_ constants
+ audio_port_role_t role;
+ if (direction == GET_DEVICES_INPUTS) {
+ role = AUDIO_PORT_ROLE_SOURCE;
+ } else if (direction == GET_DEVICES_OUTPUTS) {
+ role = AUDIO_PORT_ROLE_SINK;
+ } else {
+ ALOGE("%s invalid direction : 0x%X", __func__, direction);
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+
+ std::vector<media::AudioPortFw> deviceList;
+ AudioSystem::listDeclaredDevicePorts(static_cast<media::AudioPortRole>(role), &deviceList);
+
+ // Walk the device list
+ for (const auto &device : deviceList) {
+ ConversionResult<audio_port_v7> result = aidl2legacy_AudioPortFw_audio_port_v7(device);
+
+ struct audio_port_v7 port = VALUE_OR_RETURN_STATUS(result);
+ assert(port.type == AUDIO_PORT_TYPE_DEVICE);
+
+ env->CallVoidMethod(jDeviceTypes, gIntArrayMethods.add, port.ext.device.type);
+ }
+
+ return AUDIO_JAVA_SUCCESS;
+}
+
static int
android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz,
jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks)
@@ -3184,6 +3232,8 @@
android_media_AudioSystem_setAudioFlingerBinder),
MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I",
android_media_AudioSystem_listAudioPorts),
+ MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I",
+ android_media_AudioSystem_getSupportedDeviceTypes),
MAKE_JNI_NATIVE_METHOD("createAudioPatch",
"([Landroid/media/AudioPatch;[Landroid/media/"
"AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
@@ -3325,6 +3375,10 @@
gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, "toArray", "()[Ljava/lang/Object;");
+ jclass intArrayClass = FindClassOrDie(env, "android/util/IntArray");
+ gIntArrayClass = MakeGlobalRefOrDie(env, intArrayClass);
+ gIntArrayMethods.add = GetMethodIDOrDie(env, gIntArrayClass, "add", "(I)V");
+
jclass booleanClass = FindClassOrDie(env, "java/lang/Boolean");
gBooleanClass = MakeGlobalRefOrDie(env, booleanClass);
gBooleanCstor = GetMethodIDOrDie(env, booleanClass, "<init>", "(Z)V");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1a6bf28..aea3ca1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1276,9 +1276,17 @@
mBubbleData.setExpanded(true);
}
} else {
- // App bubble does not exist, lets add and expand it
- Log.i(TAG, " showOrHideAppBubble, creating and expanding app bubble");
- Bubble b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
+ // Check if it exists in the overflow
+ Bubble b = mBubbleData.getOverflowBubbleWithKey(appBubbleKey);
+ if (b != null) {
+ // It's in the overflow, so remove it & reinflate
+ Log.i(TAG, " showOrHideAppBubble, expanding app bubble from overflow");
+ mBubbleData.removeOverflowBubble(b);
+ } else {
+ // App bubble does not exist, lets add and expand it
+ Log.i(TAG, " showOrHideAppBubble, creating and expanding app bubble");
+ b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
+ }
b.setShouldAutoExpand(true);
inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 6e0c804..127c7e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -491,6 +491,19 @@
}
/**
+ * Explicitly removes a bubble from the overflow, if it exists.
+ *
+ * @param bubble the bubble to remove.
+ */
+ public void removeOverflowBubble(Bubble bubble) {
+ if (bubble == null) return;
+ if (mOverflowBubbles.remove(bubble)) {
+ mStateChange.removedOverflowBubble = bubble;
+ dispatchPendingChanges();
+ }
+ }
+
+ /**
* Adds a group key indicating that the summary for this group should be suppressed.
*
* @param groupKey the group key of the group whose summary should be suppressed.
@@ -1145,7 +1158,6 @@
return null;
}
- @VisibleForTesting(visibility = PRIVATE)
public Bubble getOverflowBubbleWithKey(String key) {
for (int i = 0; i < mOverflowBubbles.size(); i++) {
Bubble bubble = mOverflowBubbles.get(i);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index dab762f..fa0aba5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -1190,6 +1190,23 @@
assertThat(mBubbleData.getBubbleInStackWithKey(appBubbleKey)).isNull();
}
+ @Test
+ public void test_removeOverflowBubble() {
+ sendUpdatedEntryAtTime(mEntryA1, 2000);
+ mBubbleData.setListener(mListener);
+
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ verifyUpdateReceived();
+ assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
+
+ mBubbleData.removeOverflowBubble(mBubbleA1);
+ verifyUpdateReceived();
+
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.removedOverflowBubble).isEqualTo(mBubbleA1);
+ assertOverflowChangedTo(ImmutableList.of());
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index d55d28d..b5f7caa 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -31,6 +31,8 @@
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
+#include <sstream>
+
#include "Properties.h"
#include "RenderThread.h"
#include "pipeline/skia/ShaderCache.h"
@@ -40,7 +42,8 @@
namespace uirenderer {
namespace renderthread {
-static std::array<std::string_view, 20> sEnableExtensions{
+// Not all of these are strictly required, but are all enabled if present.
+static std::array<std::string_view, 21> sEnableExtensions{
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
@@ -61,6 +64,7 @@
VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME,
+ VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
};
static bool shouldEnableExtension(const std::string_view& extension) {
@@ -303,6 +307,15 @@
*tailPNext = ycbcrFeature;
tailPNext = &ycbcrFeature->pNext;
+ if (grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
+ VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures =
+ new VkPhysicalDeviceFaultFeaturesEXT;
+ deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
+ deviceFaultFeatures->pNext = nullptr;
+ *tailPNext = deviceFaultFeatures;
+ tailPNext = &deviceFaultFeatures->pNext;
+ }
+
// query to get the physical device features
mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
// this looks like it would slow things down,
@@ -405,6 +418,79 @@
});
}
+namespace {
+void onVkDeviceFault(const std::string& contextLabel, const std::string& description,
+ const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+ const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+ const std::vector<std::byte>& vendorBinaryData) {
+ // The final crash string should contain as much differentiating info as possible, up to 1024
+ // bytes. As this final message is constructed, the same information is also dumped to the logs
+ // but in a more verbose format. Building the crash string is unsightly, so the clearer logging
+ // statement is always placed first to give context.
+ ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", contextLabel.c_str(), description.c_str());
+ std::stringstream crashMsg;
+ crashMsg << "VK_ERROR_DEVICE_LOST (" << contextLabel;
+
+ if (!addressInfos.empty()) {
+ ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size());
+ crashMsg << ", " << addressInfos.size() << " address info (";
+ for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) {
+ ALOGE(" addressType: %d", (int)addressInfo.addressType);
+ ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress);
+ ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision);
+ crashMsg << addressInfo.addressType << ":"
+ << addressInfo.reportedAddress << ":"
+ << addressInfo.addressPrecision << ", ";
+ }
+ crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
+ crashMsg << ")";
+ }
+
+ if (!vendorInfos.empty()) {
+ ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size());
+ crashMsg << ", " << vendorInfos.size() << " vendor info (";
+ for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) {
+ ALOGE(" description: %s", vendorInfo.description);
+ ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode);
+ ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData);
+ // Omit descriptions for individual vendor info structs in the crash string, as the
+ // fault code and fault data fields should be enough for clustering, and the verbosity
+ // isn't worth it. Additionally, vendors may just set the general description field of
+ // the overall fault to the description of the first element in this list, and that
+ // overall description will be placed at the end of the crash string.
+ crashMsg << vendorInfo.vendorFaultCode << ":"
+ << vendorInfo.vendorFaultData << ", ";
+ }
+ crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
+ crashMsg << ")";
+ }
+
+ if (!vendorBinaryData.empty()) {
+ // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports
+ ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics"
+ " Stack team if you observe this message).",
+ vendorBinaryData.size());
+ crashMsg << ", " << vendorBinaryData.size() << " bytes binary";
+ }
+
+ crashMsg << "): " << description;
+ LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str());
+}
+
+void deviceLostProcRenderThread(void* callbackContext, const std::string& description,
+ const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+ const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+ const std::vector<std::byte>& vendorBinaryData) {
+ onVkDeviceFault("RenderThread", description, addressInfos, vendorInfos, vendorBinaryData);
+}
+void deviceLostProcUploadThread(void* callbackContext, const std::string& description,
+ const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+ const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+ const std::vector<std::byte>& vendorBinaryData) {
+ onVkDeviceFault("UploadThread", description, addressInfos, vendorInfos, vendorBinaryData);
+}
+} // anonymous namespace
+
static void onGrContextReleased(void* context) {
VulkanManager* manager = (VulkanManager*)context;
manager->decStrong((void*)onGrContextReleased);
@@ -430,6 +516,10 @@
backendContext.fVkExtensions = &mExtensions;
backendContext.fDeviceFeatures2 = &mPhysicalDeviceFeatures2;
backendContext.fGetProc = std::move(getProc);
+ backendContext.fDeviceLostContext = nullptr;
+ backendContext.fDeviceLostProc = (contextType == ContextType::kRenderThread)
+ ? deviceLostProcRenderThread
+ : deviceLostProcUploadThread;
LOG_ALWAYS_FATAL_IF(options.fContextDeleteProc != nullptr, "Conflicting fContextDeleteProcs!");
this->incStrong((void*)onGrContextReleased);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 4918289..69708ec 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -23,6 +23,7 @@
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
+import static android.media.audio.Flags.FLAG_SUPPORTED_DEVICE_TYPES_API;
import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import android.Manifest;
@@ -80,6 +81,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
@@ -100,6 +102,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -7838,6 +7841,51 @@
}
/**
+ * Returns a Set of unique Integers corresponding to audio device type identifiers that can
+ * <i>potentially</i> be connected to the system and meeting the criteria specified in the
+ * <code>direction</code> parameter.
+ * Note that this set contains {@link AudioDeviceInfo} device type identifiers for both devices
+ * currently available <i>and</i> those that can be available if the user connects an audio
+ * peripheral. Examples include TYPE_WIRED_HEADSET if the Android device supports an analog
+ * headset jack or TYPE_USB_DEVICE if the Android device supports a USB host-mode port.
+ * These are generally a superset of device type identifiers associated with the
+ * AudioDeviceInfo objects returned from AudioManager.getDevices().
+ * @param direction The constant specifying whether input or output devices are queried.
+ * @see #GET_DEVICES_OUTPUTS
+ * @see #GET_DEVICES_INPUTS
+ * @return A (possibly zero-length) Set of Integer objects corresponding to the audio
+ * device types of devices supported by the implementation.
+ * @throws IllegalArgumentException If an invalid direction constant is specified.
+ */
+ @FlaggedApi(FLAG_SUPPORTED_DEVICE_TYPES_API)
+ public @NonNull Set<Integer>
+ getSupportedDeviceTypes(int direction) {
+ if (direction != GET_DEVICES_OUTPUTS && direction != GET_DEVICES_INPUTS) {
+ throw new IllegalArgumentException("AudioManager.getSupportedDeviceTypes("
+ + Integer.toHexString(direction) + ") - Invalid.");
+ }
+
+ IntArray internalDeviceTypes = new IntArray();
+ int status = AudioSystem.getSupportedDeviceTypes(direction, internalDeviceTypes);
+ if (status != AudioManager.SUCCESS) {
+ Log.e(TAG, "AudioManager.getSupportedDeviceTypes(" + direction + ") failed. status:"
+ + status);
+ }
+
+ // convert to external (AudioDeviceInfo.getType()) device IDs
+ HashSet<Integer> externalDeviceTypes = new HashSet<Integer>();
+ for (int index = 0; index < internalDeviceTypes.size(); index++) {
+ // Set will eliminate any duplicates which AudioSystem.getSupportedDeviceTypes()
+ // returns
+ externalDeviceTypes.add(
+ AudioDeviceInfo.convertInternalDeviceToDeviceType(
+ internalDeviceTypes.get(index)));
+ }
+
+ return externalDeviceTypes;
+ }
+
+ /**
* Returns an array of {@link AudioDeviceInfo} objects corresponding to the audio devices
* currently connected to the system and meeting the criteria specified in the
* <code>flags</code> parameter.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 0f6cbff..f73be35f 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -36,6 +36,7 @@
import android.os.Parcel;
import android.os.Vibrator;
import android.telephony.TelephonyManager;
+import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -1947,6 +1948,8 @@
/** @hide */
public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation);
/** @hide */
+ public static native int getSupportedDeviceTypes(int flags, IntArray internalDeviceTypes);
+ /** @hide */
public static native int createAudioPatch(AudioPatch[] patch,
AudioPortConfig[] sources, AudioPortConfig[] sinks);
/** @hide */
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 3524f8c..f8b640c7 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -17,7 +17,7 @@
method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
- method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderModePollingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
method @FlaggedApi("android.nfc.enable_nfc_charging") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setWlcEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 11eb97b..4d56c11 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1764,7 +1764,9 @@
private static final int ENABLE_POLLING_FLAGS = 0x0000;
/**
- * Privileged API to enable disable reader polling.
+ * Privileged API to enable or disable reader polling.
+ * Unlike {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}, this API does not
+ * need a foreground activity to control reader mode parameters
* Note: Use with caution! The app is responsible for ensuring that the polling state is
* returned to normal.
*
@@ -1778,14 +1780,14 @@
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@SuppressLint("VisiblySynchronized")
- public void setReaderMode(boolean enablePolling) {
+ public void setReaderModePollingEnabled(boolean enable) {
synchronized (sLock) {
if (!sHasNfcFeature) {
throw new UnsupportedOperationException();
}
}
Binder token = new Binder();
- int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+ int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
try {
NfcAdapter.sService.setReaderMode(token, null, flags, null);
} catch (RemoteException e) {
diff --git a/packages/CredentialManager/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
index 7b235f8..0100718 100644
--- a/packages/CredentialManager/res/drawable/more_horiz_24px.xml
+++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
@@ -3,6 +3,7 @@
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
+ android:contentDescription="@string/more_options_content_description"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml b/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
index b97e992..fdda9ea 100644
--- a/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
+++ b/packages/CredentialManager/res/layout/credman_dropdown_bottom_sheet.xml
@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
- android:contentDescription="@string/provider_icon_content_description"
+ android:contentDescription="@string/more_options_content_description"
android:background="@null"/>
<TextView
android:id="@android:id/text1"
diff --git a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
index 261154f..c7c2fda 100644
--- a/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
+++ b/packages/CredentialManager/res/layout/credman_dropdown_presentation_layout.xml
@@ -25,7 +25,6 @@
android:id="@android:id/icon1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:contentDescription="@string/dropdown_presentation_more_sign_in_options_text"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:background="@null"/>
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index f98164b..82b47a9 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -172,5 +172,5 @@
<!-- Strings for dropdown presentation. -->
<!-- Text shown in the dropdown presentation to select more sign in options. [CHAR LIMIT=120] -->
<string name="dropdown_presentation_more_sign_in_options_text">Sign-in options</string>
- <string name="provider_icon_content_description">Credential provider icon</string>
+ <string name="more_options_content_description">More</string>
</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
index 68f1c86..02afc54 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
@@ -74,6 +74,8 @@
setMaxHeightMethodName,
context.resources.getDimensionPixelSize(
com.android.credentialmanager.R.dimen.autofill_icon_size));
+ remoteViews.setContentDescription(android.R.id.icon1, credentialEntryInfo
+ .providerDisplayName);
val drawableId =
com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
remoteViews.setInt(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index ee01bf9..c5485c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -642,6 +642,53 @@
}
@Test
+ fun isCommunalVisible() =
+ testScope.runTest {
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(
+ ObservableCommunalTransitionState.Idle(CommunalSceneKey.Blank)
+ )
+ communalRepository.setTransitionState(transitionState)
+
+ // isCommunalVisible is false when not on communal.
+ val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
+ assertThat(isCommunalVisible).isEqualTo(false)
+
+ // Start transition to communal.
+ transitionState.value =
+ ObservableCommunalTransitionState.Transition(
+ fromScene = CommunalSceneKey.Blank,
+ toScene = CommunalSceneKey.Communal,
+ progress = flowOf(0f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ // isCommunalVisible is true once transition starts.
+ assertThat(isCommunalVisible).isEqualTo(true)
+
+ // Finish transition to communal
+ transitionState.value =
+ ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+
+ // isCommunalVisible is true since we're on communal.
+ assertThat(isCommunalVisible).isEqualTo(true)
+
+ // Start transition away from communal.
+ transitionState.value =
+ ObservableCommunalTransitionState.Transition(
+ fromScene = CommunalSceneKey.Communal,
+ toScene = CommunalSceneKey.Blank,
+ progress = flowOf(1.0f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ // isCommunalVisible is still true as the false as soon as transition away runs.
+ assertThat(isCommunalVisible).isEqualTo(true)
+ }
+
+ @Test
fun testShowWidgetEditorStartsActivity() =
testScope.runTest {
underTest.showWidgetEditor()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 74c1970..2a9bc4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -28,7 +28,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
@@ -47,8 +46,6 @@
@Mock
CentralSurfaces mCentralSurfaces;
@Mock
- NotificationShadeWindowController mNotificationShadeWindowController;
- @Mock
DreamTouchHandler.TouchSession mTouchSession;
CommunalTouchHandler mTouchHandler;
@@ -59,17 +56,10 @@
MockitoAnnotations.initMocks(this);
mTouchHandler = new CommunalTouchHandler(
Optional.of(mCentralSurfaces),
- mNotificationShadeWindowController,
INITIATION_WIDTH);
}
@Test
- public void testSessionStartForcesShadeOpen() {
- mTouchHandler.onSessionStart(mTouchSession);
- verify(mNotificationShadeWindowController).setForcePluginOpen(true, mTouchHandler);
- }
-
- @Test
public void testEventPropagation() {
final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 504ded3..d6d2509 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -76,6 +76,8 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -262,6 +264,7 @@
simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
windowController = mock(),
+ deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
)
startable.start()
@@ -518,6 +521,17 @@
assertCurrentScene(SceneKey.Lockscreen)
}
+ @Test
+ fun factoryResetProtectionActive_isNotVisible() =
+ testScope.runTest {
+ val isVisible by collectLastValue(sceneContainerViewModel.isVisible)
+ assertThat(isVisible).isTrue()
+
+ kosmos.fakeDeviceProvisioningRepository.setFactoryResetProtectionActive(isActive = true)
+
+ assertThat(isVisible).isFalse()
+ }
+
/**
* Asserts that the current scene in the view-model matches what's expected.
*
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 16cb623..1abbc92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -49,6 +49,7 @@
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -112,6 +113,7 @@
simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
authenticationInteractor = dagger.Lazy { authenticationInteractor },
windowController = windowController,
+ deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
)
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fa89fcd..33bdca3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -903,6 +903,10 @@
Keep it the same as in Launcher-->
<dimen name="communal_enforced_rounded_corner_max_radius">16dp</dimen>
+ <!-- Width and height used to filter widgets displayed in the communal widget picker -->
+ <dimen name="communal_widget_picker_desired_width">464dp</dimen>
+ <dimen name="communal_widget_picker_desired_height">307dp</dimen>
+
<!-- The width/height of the unlock icon view on keyguard. -->
<dimen name="keyguard_lock_height">42dp</dimen>
<dimen name="keyguard_lock_padding">20dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 80fee64..192c9001 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -129,9 +129,18 @@
.distinctUntilChanged()
/**
- * Flow that emits a boolean if the communal UI is showing, ie. the [desiredScene] is the
- * [CommunalSceneKey.Communal].
+ * Flow that emits a boolean if the communal UI is the target scene, ie. the [desiredScene] is
+ * the [CommunalSceneKey.Communal].
+ *
+ * This will be true as soon as the desired scene is set programmatically or at whatever point
+ * during a fling that SceneTransitionLayout determines that the end state will be the communal
+ * scene. The value also does not change while flinging away until the target scene is no longer
+ * communal.
+ *
+ * If you need a flow that is only true when communal is fully showing and not in transition,
+ * use [isIdleOnCommunal].
*/
+ // TODO(b/323215860): rename to something more appropriate after cleaning up usages
val isCommunalShowing: Flow<Boolean> =
communalRepository.desiredScene.map { it == CommunalSceneKey.Communal }
@@ -146,6 +155,16 @@
it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Communal
}
+ /**
+ * Flow that emits a boolean if any portion of the communal UI is visible at all.
+ *
+ * This flow will be true during any transition and when idle on the communal scene.
+ */
+ val isCommunalVisible: Flow<Boolean> =
+ communalRepository.transitionState.map {
+ !(it is ObservableCommunalTransitionState.Idle && it.scene == CommunalSceneKey.Blank)
+ }
+
/** Callback received whenever the [SceneTransitionLayout] finishes a scene transition. */
fun onSceneChanged(newScene: CommunalSceneKey) {
communalRepository.setDesiredScene(newScene)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 54c709d..eb6dc43 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -32,6 +32,7 @@
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
+import com.android.systemui.res.R
import javax.inject.Inject
/** An Activity for editing the widgets that appear in hub mode. */
@@ -45,8 +46,10 @@
) : ComponentActivity() {
companion object {
private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
- private const val EXTRA_FILTER_STRATEGY = "filter_strategy"
- private const val FILTER_STRATEGY_GLANCEABLE_HUB = 1
+ private const val EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width"
+
+ private const val EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height"
+
private const val TAG = "EditWidgetsActivity"
const val EXTRA_PRESELECTED_KEY = "preselected_key"
}
@@ -111,11 +114,19 @@
?.let { packageName ->
try {
addWidgetActivityLauncher.launch(
- Intent(Intent.ACTION_PICK).also {
- it.setPackage(packageName)
- it.putExtra(
- EXTRA_FILTER_STRATEGY,
- FILTER_STRATEGY_GLANCEABLE_HUB
+ Intent(Intent.ACTION_PICK).apply {
+ setPackage(packageName)
+ putExtra(
+ EXTRA_DESIRED_WIDGET_WIDTH,
+ resources.getDimensionPixelSize(
+ R.dimen.communal_widget_picker_desired_width
+ )
+ )
+ putExtra(
+ EXTRA_DESIRED_WIDGET_HEIGHT,
+ resources.getDimensionPixelSize(
+ R.dimen.communal_widget_picker_desired_height
+ )
)
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index c9b56a2..05279fc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -23,7 +23,6 @@
import android.view.GestureDetector;
import android.view.MotionEvent;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -34,17 +33,14 @@
/** {@link DreamTouchHandler} responsible for handling touches to open communal hub. **/
public class CommunalTouchHandler implements DreamTouchHandler {
private final int mInitiationWidth;
- private final NotificationShadeWindowController mNotificationShadeWindowController;
private final Optional<CentralSurfaces> mCentralSurfaces;
@Inject
public CommunalTouchHandler(
Optional<CentralSurfaces> centralSurfaces,
- NotificationShadeWindowController notificationShadeWindowController,
@Named(COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth) {
mInitiationWidth = initiationWidth;
mCentralSurfaces = centralSurfaces;
- mNotificationShadeWindowController = notificationShadeWindowController;
}
@Override
@@ -60,9 +56,8 @@
}
private void handleSessionStart(CentralSurfaces surfaces, TouchSession session) {
- // Force the notification shade window open (otherwise the hub won't show while swiping).
- mNotificationShadeWindowController.setForcePluginOpen(true, this);
-
+ // Notification shade window has its own logic to be visible if the hub is open, no need to
+ // do anything here other than send touch events over.
session.registerInputListener(ev -> {
surfaces.handleDreamTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 310ec95..ad6a36c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.res.R
import javax.inject.Inject
+import kotlin.math.roundToInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -74,7 +75,18 @@
isTransitionToAod && isUdfps
}
.distinctUntilChanged()
- private val padding: Flow<Int> = udfpsOverlayInteractor.iconPadding
+
+ private val padding: Flow<Int> =
+ deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
+ if (udfpsSupported) {
+ udfpsOverlayInteractor.iconPadding
+ } else {
+ configurationInteractor.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+ }
+ }
val viewModel: Flow<ForegroundIconViewModel> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 246ccb1..37abc40 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -42,6 +42,7 @@
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
+import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
import com.android.systemui.util.println
@@ -55,6 +56,7 @@
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -81,6 +83,7 @@
private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
private val authenticationInteractor: Lazy<AuthenticationInteractor>,
private val windowController: NotificationShadeWindowController,
+ private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
) : CoreStartable {
override fun start() {
@@ -112,26 +115,33 @@
private fun hydrateVisibility() {
applicationScope.launch {
// TODO(b/296114544): Combine with some global hun state to make it visible!
- sceneInteractor.transitionState
- .mapNotNull { state ->
- when (state) {
- is ObservableTransitionState.Idle -> {
- if (state.scene != SceneKey.Gone) {
- true to "scene is not Gone"
- } else {
- false to "scene is Gone"
+ deviceProvisioningInteractor.isFactoryResetProtectionActive
+ .flatMapLatest { isFrpActive ->
+ if (isFrpActive) {
+ flowOf(false to "Factory Reset Protection is active")
+ } else {
+ sceneInteractor.transitionState
+ .mapNotNull { state ->
+ when (state) {
+ is ObservableTransitionState.Idle -> {
+ if (state.scene != SceneKey.Gone) {
+ true to "scene is not Gone"
+ } else {
+ false to "scene is Gone"
+ }
+ }
+ is ObservableTransitionState.Transition -> {
+ if (state.fromScene == SceneKey.Gone) {
+ true to "scene transitioning away from Gone"
+ } else {
+ null
+ }
+ }
+ }
}
- }
- is ObservableTransitionState.Transition -> {
- if (state.fromScene == SceneKey.Gone) {
- true to "scene transitioning away from Gone"
- } else {
- null
- }
- }
+ .distinctUntilChanged()
}
}
- .distinctUntilChanged()
.collect { (isVisible, loggingReason) ->
sceneInteractor.setVisible(isVisible, loggingReason)
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 0053474..a01ac70 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -331,8 +331,8 @@
);
collectFlow(
mWindowRootView,
- mCommunalInteractor.get().isCommunalShowing(),
- this::onCommunalShowingChanged
+ mCommunalInteractor.get().isCommunalVisible(),
+ this::onCommunalVisibleChanged
);
}
@@ -475,6 +475,9 @@
}
visible = true;
mLogger.d("Visibility forced to be true");
+ } else if (state.communalVisible) {
+ visible = true;
+ mLogger.d("Visibility forced to be true by communal");
}
if (mWindowRootView != null) {
if (visible) {
@@ -510,15 +513,15 @@
}
private void applyUserActivityTimeout(NotificationShadeWindowState state) {
- final Boolean communalShowing = state.isCommunalShowingAndNotOccluded();
+ final Boolean communalVisible = state.isCommunalVisibleAndNotOccluded();
final Boolean keyguardShowing = state.isKeyguardShowingAndNotOccluded();
long timeout = -1;
- if ((communalShowing || keyguardShowing)
+ if ((communalVisible || keyguardShowing)
&& state.statusBarState == StatusBarState.KEYGUARD
&& !state.qsExpanded) {
if (state.bouncerShowing) {
timeout = KeyguardViewMediator.AWAKE_INTERVAL_BOUNCER_MS;
- } else if (communalShowing) {
+ } else if (communalVisible) {
timeout = CommunalInteractor.AWAKE_INTERVAL_MS;
} else if (keyguardShowing) {
timeout = mLockScreenDisplayTimeout;
@@ -624,7 +627,7 @@
state.dozing,
state.scrimsVisibility,
state.backgroundBlurRadius,
- state.communalShowing
+ state.communalVisible
);
}
@@ -749,8 +752,8 @@
}
@VisibleForTesting
- void onCommunalShowingChanged(Boolean showing) {
- mCurrentState.communalShowing = showing;
+ void onCommunalVisibleChanged(Boolean visible) {
+ mCurrentState.communalVisible = visible;
apply(mCurrentState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index f9c9d83..e0a98b3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -58,15 +58,15 @@
@JvmField var dreaming: Boolean = false,
@JvmField var scrimsVisibility: Int = 0,
@JvmField var backgroundBlurRadius: Int = 0,
- @JvmField var communalShowing: Boolean = false,
+ @JvmField var communalVisible: Boolean = false,
) {
fun isKeyguardShowingAndNotOccluded(): Boolean {
return keyguardShowing && !keyguardOccluded
}
- fun isCommunalShowingAndNotOccluded(): Boolean {
- return communalShowing && !keyguardOccluded
+ fun isCommunalVisibleAndNotOccluded(): Boolean {
+ return communalVisible && !keyguardOccluded
}
/** List of [String] to be used as a [Row] with [DumpsysTableLogger]. */
@@ -99,7 +99,7 @@
dozing.toString(),
scrimsVisibility.toString(),
backgroundBlurRadius.toString(),
- communalShowing.toString(),
+ communalVisible.toString(),
)
}
@@ -140,7 +140,7 @@
dozing: Boolean,
scrimsVisibility: Int,
backgroundBlurRadius: Int,
- communalShowing: Boolean,
+ communalVisible: Boolean,
) {
buffer.advance().apply {
this.keyguardShowing = keyguardShowing
@@ -172,7 +172,7 @@
this.dozing = dozing
this.scrimsVisibility = scrimsVisibility
this.backgroundBlurRadius = backgroundBlurRadius
- this.communalShowing = communalShowing
+ this.communalVisible = communalVisible
}
}
@@ -218,7 +218,7 @@
"dozing",
"scrimsVisibility",
"backgroundBlurRadius",
- "communalShowing"
+ "communalVisible"
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index d3459b1..e40bcd5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -46,7 +46,7 @@
/**
* Adds a listener that will be notified when the panel expansion fraction has changed and
- * returns the current state in a ShadeExpansionChangeEvent for legacy purposes (b/23035507).
+ * returns the current state in a ShadeExpansionChangeEvent for legacy purposes (b/281038056).
*
* @see #addExpansionListener
*/
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index a71cf95..e619806 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -25,9 +25,10 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
+import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
+import com.android.systemui.util.kotlin.combine
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -44,7 +45,7 @@
@Inject
constructor(
@Application val scope: CoroutineScope,
- deviceProvisioningRepository: DeviceProvisioningRepository,
+ deviceProvisioningInteractor: DeviceProvisioningInteractor,
disableFlagsRepository: DisableFlagsRepository,
dozeParams: DozeParameters,
keyguardRepository: KeyguardRepository,
@@ -56,7 +57,7 @@
) : ShadeInteractor, BaseShadeInteractor by baseShadeInteractor {
override val isShadeEnabled: StateFlow<Boolean> =
combine(
- deviceProvisioningRepository.isFactoryResetProtectionActive,
+ deviceProvisioningInteractor.isFactoryResetProtectionActive,
disableFlagsRepository.disableFlags,
) { isFrpActive, isDisabledByFlags ->
isDisabledByFlags.isShadeEnabled() && !isFrpActive
@@ -83,7 +84,7 @@
powerInteractor.isAsleep,
keyguardTransitionInteractor.isInTransitionToStateWhere { it == KeyguardState.AOD },
keyguardRepository.dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING },
- deviceProvisioningRepository.isFactoryResetProtectionActive,
+ deviceProvisioningInteractor.isFactoryResetProtectionActive,
) { isAsleep, goingToSleep, isPulsing, isFrpActive ->
when {
// Touches are disabled when Factory Reset Protection is active
@@ -103,7 +104,7 @@
isShadeEnabled,
keyguardRepository.isDozing,
userSetupRepository.isUserSetUp,
- deviceProvisioningRepository.isDeviceProvisioned,
+ deviceProvisioningInteractor.isDeviceProvisioned,
) { disableFlags, isShadeEnabled, isDozing, isUserSetup, isDeviceProvisioned ->
isDeviceProvisioned &&
// Disallow QS during setup if it's a simple user switcher. (The user intends to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractor.kt
new file mode 100644
index 0000000..32cf86d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractor.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.statusbar.policy.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Encapsulates device-provisioning related business logic. */
+@SysUISingleton
+class DeviceProvisioningInteractor
+@Inject
+constructor(
+ repository: DeviceProvisioningRepository,
+) {
+ /**
+ * Whether this device has been provisioned.
+ *
+ * @see android.provider.Settings.Global.DEVICE_PROVISIONED
+ */
+ val isDeviceProvisioned: Flow<Boolean> = repository.isDeviceProvisioned
+
+ /** Whether Factory Reset Protection (FRP) is currently active, locking the device. */
+ val isFactoryResetProtectionActive: Flow<Boolean> = repository.isFactoryResetProtectionActive
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index b3e386e..cc27cbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -189,7 +189,6 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
@@ -411,7 +410,7 @@
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
- new FakeDeviceProvisioningRepository(),
+ mKosmos.getDeviceProvisioningInteractor(),
new FakeDisableFlagsRepository(),
mDozeParameters,
mFakeKeyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 461db8e..7f4508a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -99,7 +99,6 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
@@ -260,7 +259,7 @@
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
- new FakeDeviceProvisioningRepository(),
+ mKosmos.getDeviceProvisioningInteractor(),
new FakeDisableFlagsRepository(),
mock(DozeParameters.class),
keyguardRepository,
@@ -452,11 +451,11 @@
}
@Test
- public void setCommunalShowing_userTimeout() {
+ public void setCommunalVisible_userTimeout() {
setKeyguardShowing();
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.onCommunalShowingChanged(true);
+ mNotificationShadeWindowController.onCommunalVisibleChanged(true);
verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
assertThat(mLayoutParameters.getValue().userActivityTimeout)
.isEqualTo(CommunalInteractor.AWAKE_INTERVAL_MS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index 3e0a647..7bd9d92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -103,7 +103,6 @@
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
@@ -205,9 +204,7 @@
mStatusBarStateController = mKosmos.getStatusBarStateController();
mInteractionJankMonitor = mKosmos.getInteractionJankMonitor();
- FakeDeviceProvisioningRepository deviceProvisioningRepository =
- new FakeDeviceProvisioningRepository();
- deviceProvisioningRepository.setDeviceProvisioned(true);
+ mKosmos.getFakeDeviceProvisioningRepository().setDeviceProvisioned(true);
FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
@@ -294,7 +291,7 @@
mShadeInteractor = new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
- deviceProvisioningRepository,
+ mKosmos.getDeviceProvisioningInteractor(),
mDisableFlagsRepository,
mDozeParameters,
mKeyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 8cb064d..5450537 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -56,8 +56,8 @@
import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.flow.emptyFlow
@@ -192,7 +192,7 @@
shadeInteractor =
ShadeInteractorImpl(
testScope.backgroundScope,
- FakeDeviceProvisioningRepository(),
+ kosmos.deviceProvisioningInteractor,
FakeDisableFlagsRepository(),
mock(),
keyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 0a3c2d9..9ea4142 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -494,7 +494,7 @@
mShadeInteractor =
new ShadeInteractorImpl(
mTestScope.getBackgroundScope(),
- deviceProvisioningRepository,
+ mKosmos.getDeviceProvisioningInteractor(),
new FakeDisableFlagsRepository(),
mDozeParameters,
keyguardRepository,
@@ -2098,6 +2098,26 @@
}
@Test
+ public void testShowOrHideAppBubble_addsFromOverflow() {
+ String appBubbleKey = Bubble.getAppBubbleKeyForApp(mAppBubbleIntent.getPackage(), mUser0);
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+
+ // Collapse the stack so we don't need to wait for the dismiss animation in the test
+ mBubbleController.collapseStack();
+
+ // Dismiss the app bubble so it's in the overflow
+ mBubbleController.dismissBubble(appBubbleKey, Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNotNull();
+
+ // Calling this while collapsed will re-add and expand the app bubble
+ mBubbleController.showOrHideAppBubble(mAppBubbleIntent, mUser0, mAppBubbleIcon);
+ assertThat(mBubbleData.getSelectedBubble().getKey()).isEqualTo(appBubbleKey);
+ assertThat(mBubbleController.isStackExpanded()).isTrue();
+ assertThat(mBubbleData.getBubbles().size()).isEqualTo(1);
+ assertThat(mBubbleData.getOverflowBubbleWithKey(appBubbleKey)).isNull();
+ }
+
+ @Test
public void testCreateBubbleFromOngoingNotification() {
NotificationEntry notif = new NotificationEntryBuilder()
.setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 083de10..b9a3d38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -43,6 +43,8 @@
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.statusbar.phone.screenOffAnimationController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.util.time.systemClock
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -80,6 +82,8 @@
val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
val communalInteractor by lazy { kosmos.communalInteractor }
val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin }
+ val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor }
+ val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
init {
kosmos.applicationContext = testCase.context
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index afd37b3..2bd76be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -29,8 +29,8 @@
import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.dozeParameters
-import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
import com.android.systemui.statusbar.policy.data.repository.userSetupRepository
+import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.user.domain.interactor.userSwitcherInteractor
var Kosmos.baseShadeInteractor: BaseShadeInteractor by
@@ -63,7 +63,7 @@
Kosmos.Fixture {
ShadeInteractorImpl(
scope = applicationCoroutineScope,
- deviceProvisioningRepository = deviceProvisioningRepository,
+ deviceProvisioningInteractor = deviceProvisioningInteractor,
disableFlagsRepository = disableFlagsRepository,
dozeParams = dozeParameters,
keyguardRepository = fakeKeyguardRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractorKosmos.kt
new file mode 100644
index 0000000..84bd3e8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/DeviceProvisioningInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.statusbar.policy.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository
+
+val Kosmos.deviceProvisioningInteractor by Fixture {
+ DeviceProvisioningInteractor(
+ repository = deviceProvisioningRepository,
+ )
+}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index a796544..458fd82 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -1292,7 +1292,7 @@
return;
}
if (DEBUG) Slog.v(TAG, "Setting NFC reader mode. enablePolling: " + enablePolling);
- nfcAdapter.setReaderMode(enablePolling);
+ nfcAdapter.setReaderModePollingEnabled(enablePolling);
}
private static int[] toArray(Collection<Integer> c) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 2e0546e..1128d0c 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1319,9 +1319,33 @@
try {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r != null) {
+ if (r == null) return;
+ final TransitionController controller = r.mTransitionController;
+ if (!controller.isShellTransitionsEnabled()) {
r.setShowWhenLocked(showWhenLocked);
+ return;
}
+ if (controller.isCollecting()
+ && !mService.mKeyguardController.isKeyguardLocked(r.getDisplayId())) {
+ // Keyguard isn't locked, so this can be done as part of the collecting
+ // transition.
+ r.setShowWhenLocked(showWhenLocked);
+ return;
+ }
+ final Transition transition = new Transition(
+ showWhenLocked ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
+ 0 /* flags */, controller, mService.mWindowManager.mSyncEngine);
+ r.mTransitionController.startCollectOrQueue(transition, (deferred) -> {
+ transition.collect(r);
+ r.setShowWhenLocked(showWhenLocked);
+ if (transition.isNoOp()) {
+ transition.abort();
+ return;
+ }
+ controller.requestStartTransition(transition, null /* trigger */,
+ null /* remoteTransition */, null /* displayChange */);
+ transition.setReady(r, true);
+ });
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -1334,9 +1358,34 @@
try {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r != null) {
+ if (r == null) return;
+ final TransitionController controller = r.mTransitionController;
+ // If shell transitions is not enabled just set it directly.
+ if (!controller.isShellTransitionsEnabled()) {
r.setInheritShowWhenLocked(inheritShowWhenLocked);
+ return;
}
+ if (controller.isCollecting()
+ && !mService.mKeyguardController.isKeyguardLocked(r.getDisplayId())) {
+ // Keyguard isn't locked, so this can be done as part of the collecting
+ // transition.
+ r.setInheritShowWhenLocked(inheritShowWhenLocked);
+ return;
+ }
+ final Transition transition = new Transition(
+ inheritShowWhenLocked ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
+ 0 /* flags */, controller, mService.mWindowManager.mSyncEngine);
+ r.mTransitionController.startCollectOrQueue(transition, (deferred) -> {
+ transition.collect(r);
+ r.setInheritShowWhenLocked(inheritShowWhenLocked);
+ if (transition.isNoOp()) {
+ transition.abort();
+ return;
+ }
+ controller.requestStartTransition(transition, null /* trigger */,
+ null /* remoteTransition */, null /* displayChange */);
+ transition.setReady(r, true);
+ });
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 2accf9a..1e58306 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2917,6 +2917,26 @@
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_WINDOW_MANAGER, TAG, cookie);
}
+ /**
+ * Get whether the transition, in its current state, is a no-op. This should be avoided. It is
+ * only here for legacy usages where we can't tell ahead-of-time whether something will
+ * generate a change.
+ */
+ boolean isNoOp() {
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ // This is the same criteria as the rejection logic in calculateTargets
+ final WindowContainer<?> wc = mParticipants.valueAt(i);
+ if (!wc.isAttached()) continue;
+ // The level of transition target should be at least window token.
+ if (wc.asWindowState() != null) continue;
+ final ChangeInfo changeInfo = mChanges.get(wc);
+ // Reject no-ops
+ if (!changeInfo.hasChanged()) continue;
+ return false;
+ }
+ return true;
+ }
+
@VisibleForTesting
static class ChangeInfo {
private static final int FLAG_NONE = 0;
diff --git a/services/profcollect/OWNERS b/services/profcollect/OWNERS
index b380e39..be9e61f 100644
--- a/services/profcollect/OWNERS
+++ b/services/profcollect/OWNERS
@@ -1,3 +1 @@
-srhines@google.com
-yabinc@google.com
-yikong@google.com
+include platform/prebuilts/clang/host/linux-x86:/OWNERS
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 582b712..fb0fbe8 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -62,7 +62,7 @@
private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
private static final String INTENT_UPLOAD_PROFILES =
"com.android.server.profcollect.UPLOAD_PROFILES";
- private static final long BG_PROCESS_PERIOD = TimeUnit.HOURS.toMillis(4); // every 4 hours.
+ private static final long BG_PROCESS_INTERVAL = TimeUnit.HOURS.toMillis(4); // every 4 hours.
private IProfCollectd mIProfcollect;
private static ProfcollectForwardingService sSelfService;
@@ -226,7 +226,7 @@
js.schedule(new JobInfo.Builder(JOB_IDLE_PROCESS, JOB_SERVICE_NAME)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
- .setPeriodic(BG_PROCESS_PERIOD)
+ .setPeriodic(BG_PROCESS_INTERVAL)
.setPriority(JobInfo.PRIORITY_MIN)
.build());
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
index 07dd59d2..a4628ee 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceTest.java
@@ -18,6 +18,8 @@
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.content.Context.DEVICE_ID_INVALID;
@@ -135,4 +137,34 @@
when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_SENSORS)).thenReturn(DEVICE_POLICY_CUSTOM);
assertThat(virtualDevice.hasCustomSensorSupport()).isTrue();
}
+
+ @Test
+ public void virtualDevice_hasCustomAudioInputSupport() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+
+ VirtualDevice virtualDevice =
+ new VirtualDevice(
+ mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
+
+ when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO)).thenReturn(DEVICE_POLICY_DEFAULT);
+ assertThat(virtualDevice.hasCustomAudioInputSupport()).isFalse();
+
+ when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_AUDIO)).thenReturn(DEVICE_POLICY_CUSTOM);
+ assertThat(virtualDevice.hasCustomAudioInputSupport()).isTrue();
+ }
+
+ @Test
+ public void virtualDevice_hasCustomCameraSupport() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_VDM_PUBLIC_APIS);
+
+ VirtualDevice virtualDevice =
+ new VirtualDevice(
+ mVirtualDevice, VIRTUAL_DEVICE_ID, /*persistentId=*/null, /*name=*/null);
+
+ when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_CAMERA)).thenReturn(DEVICE_POLICY_DEFAULT);
+ assertThat(virtualDevice.hasCustomCameraSupport()).isFalse();
+
+ when(mVirtualDevice.getDevicePolicy(POLICY_TYPE_CAMERA)).thenReturn(DEVICE_POLICY_CUSTOM);
+ assertThat(virtualDevice.hasCustomCameraSupport()).isTrue();
+ }
}