summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--INPUT_OWNERS1
-rw-r--r--TEST_MAPPING11
-rw-r--r--apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java4
-rw-r--r--apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java4
-rw-r--r--apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java10
-rw-r--r--apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java5
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/Properties.cpp8
-rw-r--r--libs/hwui/Properties.h3
-rw-r--r--libs/hwui/aconfig/hwui_flags.aconfig7
-rw-r--r--libs/hwui/renderthread/EglManager.cpp5
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java8
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt88
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt9
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt274
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java15
-rw-r--r--services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java127
-rw-r--r--services/core/java/com/android/server/wm/AppCompatRoundedCorners.java143
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java19
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp255
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java57
39 files changed, 768 insertions, 454 deletions
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index 06ead06fc13a..9b1016e7b7e9 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -1,4 +1,5 @@
# Bug component: 136048
+# Please assign bugs to android-framework-input-triage@.
arpitks@google.com
asmitapoddar@google.com
hcutts@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 49384cde5803..5db077220b20 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -137,13 +137,14 @@
"name": "CtsStrictJavaPackagesTestCases"
}
],
- "postsubmit-ravenwood": [
+ "ravenwood-presubmit": [
{
"name": "CtsUtilTestCasesRavenwood",
- "host": true,
- "file_patterns": [
- "[Rr]avenwood"
- ]
+ "host": true
+ },
+ {
+ "name": "RavenwoodBivalentTest",
+ "host": true
}
],
"postsubmit-managedprofile-stress": [
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index d7b1c9a2d3a2..f20b1706129b 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -43,7 +43,6 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -143,7 +142,7 @@ public final class ClientSocketPerfTest {
// Always use the same server for consistency across the benchmarks.
server = config.serverFactory().newServer(
- ChannelType.CHANNEL, config.messageSize(), config.protocol().getProtocols(),
+ config.messageSize(), config.protocol().getProtocols(),
ciphers(config));
server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
@@ -197,7 +196,6 @@ public final class ClientSocketPerfTest {
*/
@Test
@Parameters(method = "getParams")
- @Ignore("b/351034205")
public void time(Config config) throws Exception {
reset();
setup(config);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
index 0655f45726ba..ba2acb8a5205 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
@@ -43,10 +43,10 @@ public enum EndpointFactory {
factories.clientFactory, channelType, port, protocols, ciphers);
}
- public ServerEndpoint newServer(ChannelType channelType, int messageSize,
+ public ServerEndpoint newServer(int messageSize,
String[] protocols, String[] ciphers) throws IOException {
return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
- channelType, messageSize, protocols, ciphers);
+ messageSize, protocols, ciphers);
}
private static final class Factories {
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
index 3631c3f29287..1e4f12460936 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
@@ -34,8 +34,6 @@ import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
-import org.conscrypt.ChannelType;
-
/**
* A simple socket-based test server.
*/
@@ -63,7 +61,6 @@ final class ServerEndpoint {
}
private final ServerSocket serverSocket;
- private final ChannelType channelType;
private final SSLSocketFactory socketFactory;
private final int messageSize;
private final String[] protocols;
@@ -78,11 +75,10 @@ final class ServerEndpoint {
private volatile Future<?> processFuture;
ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
- ChannelType channelType, int messageSize, String[] protocols,
+ int messageSize, String[] protocols,
String[] cipherSuites) throws IOException {
- this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+ this.serverSocket = serverSocketFactory.createServerSocket();
this.socketFactory = socketFactory;
- this.channelType = channelType;
this.messageSize = messageSize;
this.protocols = protocols;
this.cipherSuites = cipherSuites;
@@ -134,7 +130,7 @@ final class ServerEndpoint {
if (stopping) {
return;
}
- socket = channelType.accept(serverSocket, socketFactory);
+ socket = (SSLSocket) serverSocket.accept();
socket.setEnabledProtocols(protocols);
socket.setEnabledCipherSuites(cipherSuites);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index 8916a3c55a9a..af3c405eab82 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -43,7 +43,6 @@ import androidx.test.filters.LargeTest;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -131,8 +130,7 @@ public final class ServerSocketPerfTest {
final ChannelType channelType = config.channelType();
- server = config.serverFactory().newServer(
- channelType, config.messageSize(),
+ server = config.serverFactory().newServer(config.messageSize(),
new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
server.setMessageProcessor(new MessageProcessor() {
@Override
@@ -202,7 +200,6 @@ public final class ServerSocketPerfTest {
@Test
@Parameters(method = "getParams")
- @Ignore("b/351034205")
public void throughput(Config config) throws Exception {
setup(config);
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d71f3b6884ae..23cd3ce965ff 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -143,6 +143,7 @@ cc_defaults {
"aconfig_text_flags_c_lib",
"server_configurable_flags",
"libaconfig_storage_read_api_cc",
+ "libgraphicsenv",
],
static_libs: [
"libEGL_blobCache",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 1217b47664dd..b6476c9d466f 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -42,6 +42,11 @@ constexpr bool hdr_10bit_plus() {
constexpr bool initialize_gl_always() {
return false;
}
+
+constexpr bool skip_eglmanager_telemetry() {
+ return false;
+}
+
constexpr bool resample_gainmap_regions() {
return false;
}
@@ -103,6 +108,7 @@ float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number
bool Properties::clipSurfaceViews = false;
bool Properties::hdr10bitPlus = false;
+bool Properties::skipTelemetry = false;
bool Properties::resampleGainmapRegions = false;
int Properties::timeoutMultiplier = 1;
@@ -183,6 +189,8 @@ bool Properties::load() {
hwui_flags::resample_gainmap_regions());
timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+ skipTelemetry = base::GetBoolProperty(PROPERTY_SKIP_EGLMANAGER_TELEMETRY,
+ hwui_flags::skip_eglmanager_telemetry());
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 73e80ce4afd0..db471527b861 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -234,6 +234,8 @@ enum DebugLevel {
*/
#define PROPERTY_INITIALIZE_GL_ALWAYS "debug.hwui.initialize_gl_always"
+#define PROPERTY_SKIP_EGLMANAGER_TELEMETRY "debug.hwui.skip_eglmanager_telemetry"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -342,6 +344,7 @@ public:
static bool clipSurfaceViews;
static bool hdr10bitPlus;
+ static bool skipTelemetry;
static bool resampleGainmapRegions;
static int timeoutMultiplier;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 13c0b00daa21..a1f51687b077 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -99,6 +99,13 @@ flag {
}
flag {
+ name: "skip_eglmanager_telemetry"
+ namespace: "core_graphics"
+ description: "Skip telemetry in EglManager's calls to eglCreateContext to avoid polluting telemetry"
+ bug: "347911216"
+}
+
+flag {
name: "resample_gainmap_regions"
namespace: "core_graphics"
description: "Resample gainmaps when decoding regions, to improve visual quality"
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 708b0113e13e..60104452f4c0 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -19,6 +19,7 @@
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <cutils/properties.h>
+#include <graphicsenv/GpuStatsInfo.h>
#include <log/log.h>
#include <sync/sync.h>
#include <utils/Trace.h>
@@ -366,6 +367,10 @@ void EglManager::createContext() {
contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
contextAttributes.push_back(Properties::contextPriority);
}
+ if (Properties::skipTelemetry) {
+ contextAttributes.push_back(EGL_TELEMETRY_HINT_ANDROID);
+ contextAttributes.push_back(android::GpuStatsInfo::SKIP_TELEMETRY);
+ }
contextAttributes.push_back(EGL_NONE);
mEglContext = eglCreateContext(
mEglDisplay, EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 9ad3e3c0af0f..170cb4546d0c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -91,8 +91,7 @@ public class UninstallerActivity extends Activity {
// be stale, if e.g. the app was uninstalled while the activity was destroyed.
super.onCreate(null);
- // TODO(b/318521110) Enable PIA v2 for archive dialog.
- if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) {
+ if (usePiaV2() && !isTv()) {
Log.i(TAG, "Using Pia V2");
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
@@ -225,11 +224,6 @@ public class UninstallerActivity extends Activity {
showConfirmationDialog();
}
- private boolean isArchiveDialog(Intent intent) {
- return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
- & PackageManager.DELETE_ARCHIVE) != 0;
- }
-
/**
* Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
* to archive an app if requested.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 186b69b4107b..3b0faf0cb56c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -736,7 +736,8 @@ class InstallRepository(private val context: Context) {
val appInfo = packageManager.getApplicationInfo(
pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
)
- if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
+ // If the package is archived, treat it as an update case.
+ if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
return false
}
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
index 0091a3e8b2cf..96525f645004 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -32,6 +32,8 @@ import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
+import android.content.pm.PackageManager.PackageInfoFlags
import android.content.pm.VersionedPackage
import android.graphics.drawable.Icon
import android.os.Build
@@ -51,6 +53,9 @@ import com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionF
import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame
+import com.android.packageinstaller.v2.model.UninstallAborted.Companion.ABORT_REASON_UNINSTALL_DONE
+import android.content.pm.Flags as PmFlags
+import android.multiuser.Flags as MultiuserFlags
class UninstallRepository(private val context: Context) {
@@ -71,6 +76,7 @@ class UninstallRepository(private val context: Context) {
private var uninstallFromAllUsers = false
private var isClonedApp = false
private var uninstallId = 0
+ private var deleteFlags = 0
fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage {
this.intent = intent
@@ -155,7 +161,9 @@ class UninstallRepository(private val context: Context) {
try {
targetAppInfo = packageManager.getApplicationInfo(
targetPackageName!!,
- PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong())
+ ApplicationInfoFlags.of(
+ PackageManager.MATCH_ANY_USER.toLong() or PackageManager.MATCH_ARCHIVED_PACKAGES
+ )
)
} catch (e: PackageManager.NameNotFoundException) {
Log.e(LOG_TAG, "Unable to get packageName")
@@ -180,9 +188,27 @@ class UninstallRepository(private val context: Context) {
}
}
+ parseDeleteFlags(intent)
+
return UninstallReady()
}
+ /**
+ * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
+ * to archive an app if requested.
+ *
+ * Do not parse other flags because developers might pass here any flags which might cause
+ * unintended behaviour.
+ * For more context {@link com.android.server.pm.PackageArchiver#requestArchive}.
+ */
+ private fun parseDeleteFlags(intent: Intent) {
+ val flags = intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
+ val archive = flags and PackageManager.DELETE_ARCHIVE
+ val keepData = flags and PackageManager.DELETE_KEEP_DATA
+
+ deleteFlags = archive or keepData
+ }
+
fun generateUninstallDetails(): UninstallStage {
val messageBuilder = StringBuilder()
@@ -201,6 +227,8 @@ class UninstallRepository(private val context: Context) {
}
val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+ val isArchive =
+ PmFlags.archiving() && ((deleteFlags and PackageManager.DELETE_ARCHIVE) != 0)
val myUserHandle = Process.myUserHandle()
val isSingleUser = isSingleUser()
@@ -215,34 +243,54 @@ class UninstallRepository(private val context: Context) {
)
)
} else if (uninstallFromAllUsers && !isSingleUser) {
- messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users))
+ val messageString = if (isArchive) {
+ context.getString(R.string.archive_application_text_all_users)
+ } else {
+ context.getString(R.string.uninstall_application_text_all_users)
+ }
+ messageBuilder.append(messageString)
} else if (uninstalledUser != myUserHandle) {
// Uninstalling user is issuing uninstall for another user
val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
.getSystemService(UserManager::class.java)
val userName = customUserManager!!.userName
- var messageString = context.getString(
- R.string.uninstall_application_text_user,
- userName
- )
+
+ var messageString = if (isArchive) {
+ context.getString(R.string.archive_application_text_user, userName)
+ } else {
+ context.getString(R.string.uninstall_application_text_user, userName)
+ }
+
if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) {
if (customUserManager.isManagedProfile) {
- messageString = context.getString(
+ messageString = if (isArchive) {
+ context.getString(
+ R.string.archive_application_text_current_user_work_profile, userName
+ )
+ } else {
+ context.getString(
R.string.uninstall_application_text_current_user_work_profile, userName
- )
+ )
+ }
} else if (customUserManager.isCloneProfile){
isClonedApp = true
messageString = context.getString(
R.string.uninstall_application_text_current_user_clone_profile
)
} else if (Flags.allowPrivateProfile()
- && android.multiuser.Flags.enablePrivateSpaceFeatures()
+ && MultiuserFlags.enablePrivateSpaceFeatures()
&& customUserManager.isPrivateProfile
) {
// TODO(b/324244123): Get these Strings from a User Property API.
- messageString = context.getString(
+ messageString = if (isArchive) {
+ context.getString(
+ R.string.archive_application_text_current_user_private_profile, userName
+ )
+ } else {
+ context.getString(
R.string.uninstall_application_text_current_user_private_profile
- )
+ )
+ }
}
}
messageBuilder.append(messageString)
@@ -262,6 +310,8 @@ class UninstallRepository(private val context: Context) {
targetAppLabel
)
)
+ } else if (isArchive) {
+ messageBuilder.append(context.getString(R.string.archive_application_text))
} else {
messageBuilder.append(context.getString(R.string.uninstall_application_text))
}
@@ -270,15 +320,21 @@ class UninstallRepository(private val context: Context) {
val title = if (isClonedApp) {
context.getString(R.string.cloned_app_label, targetAppLabel)
+ } else if (isArchive) {
+ context.getString(R.string.archiving_app_label, targetAppLabel)
} else {
targetAppLabel.toString()
}
var suggestToKeepAppData = false
try {
- val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0)
+ val pkgInfo = packageManager.getPackageInfo(
+ targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES)
+ )
suggestToKeepAppData =
- pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData()
+ pkgInfo.applicationInfo != null
+ && pkgInfo.applicationInfo!!.hasFragileUserData()
+ && !isArchive
} catch (e: PackageManager.NameNotFoundException) {
Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e)
}
@@ -291,7 +347,7 @@ class UninstallRepository(private val context: Context) {
)
}
- return UninstallUserActionRequired(title, message, appDataSize)
+ return UninstallUserActionRequired(title, message, appDataSize, isArchive)
}
/**
@@ -444,10 +500,11 @@ class UninstallRepository(private val context: Context) {
callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message)
// Since the caller already received the results, just finish the app at this point
- uninstallResult.value = null
+ uninstallResult.value = UninstallAborted(ABORT_REASON_UNINSTALL_DONE)
return
}
val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+
if (returnResult || callingActivity != null) {
val intent = Intent()
intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus)
@@ -717,6 +774,7 @@ class UninstallRepository(private val context: Context) {
): Boolean {
var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0
flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0
+ flags = flags or deleteFlags
return try {
context.createContextAsUser(targetUser, 0)
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
index f086209fe498..316e8b7e3a73 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
@@ -38,7 +38,8 @@ class UninstallReady : UninstallStage(STAGE_READY)
data class UninstallUserActionRequired(
val title: String? = null,
val message: String? = null,
- val appDataSize: Long = 0
+ val appDataSize: Long = 0,
+ val isArchive: Boolean = false
) : UninstallStage(STAGE_USER_ACTION_REQUIRED)
data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) :
@@ -96,6 +97,11 @@ data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED
dialogTextResource = R.string.user_is_not_allowed_dlg_text
}
+ ABORT_REASON_UNINSTALL_DONE -> {
+ dialogTitleResource = 0
+ dialogTextResource = 0
+ }
+
else -> {
dialogTitleResource = 0
dialogTextResource = R.string.generic_error_dlg_text
@@ -107,6 +113,7 @@ data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED
const val ABORT_REASON_GENERIC_ERROR = 0
const val ABORT_REASON_APP_UNAVAILABLE = 1
const val ABORT_REASON_USER_NOT_ALLOWED = 2
+ const val ABORT_REASON_UNINSTALL_DONE = 3
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
index 87af1ae200ca..524b4e6a0e63 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
@@ -60,7 +60,7 @@ public class UninstallConfirmationFragment extends DialogFragment {
Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
.setTitle(mDialogData.getTitle())
- .setPositiveButton(R.string.ok,
+ .setPositiveButton(mDialogData.isArchive() ? R.string.archive : R.string.ok,
(dialogInt, which) -> mUninstallActionListener.onPositiveResponse(
mKeepData != null && mKeepData.isChecked()))
.setNegativeButton(R.string.cancel,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 03149928249b..17c3fd124f66 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -493,6 +493,16 @@ flag {
}
flag {
+ name: "status_bar_switch_to_spn_from_data_spn"
+ namespace: "systemui"
+ description: "Fix usage of the SPN broadcast extras"
+ bug: "350812372"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "haptic_volume_slider"
namespace: "systemui"
description: "Adds haptic feedback to the volume slider."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 85d5d6a41965..808e6666e6e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -26,7 +26,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.layout.layout
-import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.viewinterop.AndroidView
@@ -59,16 +58,8 @@ fun SceneScope.MediaCarousel(
return
}
- val density = LocalDensity.current
val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
- val layoutWidth = 0
- val layoutHeight = with(density) { mediaHeight.toPx() }.toInt()
-
- // Notify controller to size the carousel for the current space
- mediaHost.measurementInput = MeasurementInput(layoutWidth, layoutHeight)
- carouselController.setSceneContainerSize(layoutWidth, layoutHeight)
-
MovableElement(
key = MediaCarousel.Elements.Content,
modifier = modifier.height(mediaHeight).fillMaxWidth(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index c5d7b25827ea..46c5c188344e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -888,8 +888,6 @@ constructor(
heightInSceneContainerPx = height
mediaCarouselScrollHandler.playerWidthPlusPadding =
width + context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
- mediaContent.minimumWidth = widthInSceneContainerPx
- mediaContent.minimumHeight = heightInSceneContainerPx
updatePlayers(recreateMedia = true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 88a28bf998da..091b886c7ba4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -1210,7 +1210,6 @@ constructor(
(onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding
val location =
when {
- mediaFlags.isSceneContainerEnabled() -> desiredLocation
dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
onCommunal -> LOCATION_COMMUNAL_HUB
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b48b40986f73..875e505db1c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -94,6 +94,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
@@ -173,6 +174,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
private float mActiveRadius;
private FeatureFlags mFeatureFlags;
private UserTracker mUserTracker;
+ private VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor;
public enum BroadcastNotifyDialog {
ACTION_FIRST_LAUNCH,
@@ -195,6 +197,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
PowerExemptionManager powerExemptionManager,
KeyguardManager keyGuardManager,
FeatureFlags featureFlags,
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor,
UserTracker userTracker) {
mContext = context;
mPackageName = packageName;
@@ -209,6 +212,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mFeatureFlags = featureFlags;
mUserTracker = userTracker;
mToken = token;
+ mVolumePanelGlobalStateInteractor = volumePanelGlobalStateInteractor;
InfoMediaManager imm =
InfoMediaManager.createInstance(mContext, packageName, userHandle, lbm, token);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
@@ -436,7 +440,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
launchIntent.putExtra(EXTRA_ROUTE_ID, routeId);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCallback.dismissDialog();
- mActivityStarter.startActivity(launchIntent, true, controller);
+ startActivity(launchIntent, controller);
}
}
@@ -447,7 +451,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
if (launchIntent != null) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mCallback.dismissDialog();
- mActivityStarter.startActivity(launchIntent, true, controller);
+ startActivity(launchIntent, controller);
}
}
@@ -951,10 +955,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
deepLinkIntent.putExtra(
Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
PAGE_CONNECTED_DEVICES_KEY);
- mActivityStarter.startActivity(deepLinkIntent, true, controller);
+ startActivity(deepLinkIntent, controller);
return;
}
- mActivityStarter.startActivity(launchIntent, true, controller);
+ startActivity(launchIntent, controller);
}
void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
@@ -998,6 +1002,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mPowerExemptionManager,
mKeyGuardManager,
mFeatureFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
broadcastSender, controller);
@@ -1244,6 +1249,13 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
return !device.isVolumeFixed();
}
+ private void startActivity(Intent intent, ActivityTransitionAnimator.Controller controller) {
+ // Media Output dialog can be shown from the volume panel. This makes sure the panel is
+ // closed when navigating to another activity, so it doesn't stays on top of it
+ mVolumePanelGlobalStateInteractor.setVisible(false);
+ mActivityStarter.startActivity(intent, true, controller);
+ }
+
@Override
public void onDevicesUpdated(List<NearbyDevice> nearbyDevices) throws RemoteException {
mNearbyDeviceInfoMap.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 104d4b5427d4..65a59f50d1bf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1339,6 +1339,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
"NotificationPanelViewController.updateResources");
if (splitShadeChanged) {
+ if (isPanelVisibleBecauseOfHeadsUp()) {
+ // workaround for b/324642496, because HUNs set state to OPENING
+ onPanelStateChanged(STATE_CLOSED);
+ }
onSplitShadeEnabledChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9d13a17d8e02..cb3e26b9f8ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1804,6 +1804,20 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
NotificationEntry childEntry,
NotificationEntry containerEntry
);
+
+ /**
+ * Called when resetting the alpha value for content views
+ */
+ void logResetAllContentAlphas(
+ NotificationEntry entry
+ );
+
+ /**
+ * Called when resetting the alpha value for content views is skipped
+ */
+ void logSkipResetAllContentAlphas(
+ NotificationEntry entry
+ );
}
/**
@@ -3001,6 +3015,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.animate().cancel();
}
resetAllContentAlphas();
+ } else {
+ mLogger.logSkipResetAllContentAlphas(getEntry());
}
mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
updateChildrenVisibility();
@@ -3186,6 +3202,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
protected void resetAllContentAlphas() {
+ mLogger.logResetAllContentAlphas(getEntry());
mPrivateLayout.setAlpha(1f);
mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
mPublicLayout.setAlpha(1f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 4c76e3284dbe..c31a2cb8908b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -195,6 +195,20 @@ public class ExpandableNotificationRowController implements NotifViewController
) {
mLogBufferLogger.logRemoveTransientRow(childEntry, containerEntry);
}
+
+ @Override
+ public void logResetAllContentAlphas(
+ NotificationEntry entry
+ ) {
+ mLogBufferLogger.logResetAllContentAlphas(entry);
+ }
+
+ @Override
+ public void logSkipResetAllContentAlphas(
+ NotificationEntry entry
+ ) {
+ mLogBufferLogger.logSkipResetAllContentAlphas(entry);
+ }
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index 4f5a04f2bdc9..b1e90329e01a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -128,6 +128,24 @@ constructor(
{ "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
)
}
+
+ fun logResetAllContentAlphas(entry: NotificationEntry) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = entry.logKey },
+ { "resetAllContentAlphas: $str1" }
+ )
+ }
+
+ fun logSkipResetAllContentAlphas(entry: NotificationEntry) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = entry.logKey },
+ { "Skip resetAllContentAlphas: $str1" }
+ )
+ }
}
private const val TAG = "NotifRow"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 2e54972c4950..9cbfc440ab16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -198,7 +198,8 @@ constructor(
fun logServiceProvidersUpdatedBroadcast(intent: Intent) {
val showSpn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)
- val spn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
+ val spn = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
+ val dataSpn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
val showPlmn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)
val plmn = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
@@ -208,12 +209,13 @@ constructor(
{
bool1 = showSpn
str1 = spn
+ str2 = dataSpn
bool2 = showPlmn
- str2 = plmn
+ str3 = plmn
},
{
"Intent: ACTION_SERVICE_PROVIDERS_UPDATED." +
- " showSpn=$bool1 spn=$str1 showPlmn=$bool2 plmn=$str2"
+ " showSpn=$bool1 spn=$str1 dataSpn=$str2 showPlmn=$bool2 plmn=$str3"
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 99ed2d99c749..85bbe7e53493 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -21,6 +21,8 @@ import android.telephony.TelephonyManager.EXTRA_DATA_SPN
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
+import android.telephony.TelephonyManager.EXTRA_SPN
+import com.android.systemui.Flags.statusBarSwitchToSpnFromDataSpn
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
@@ -96,7 +98,13 @@ sealed interface NetworkNameModel : Diffable<NetworkNameModel> {
fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? {
val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
- val spn = getStringExtra(EXTRA_DATA_SPN)
+ val spn =
+ if (statusBarSwitchToSpnFromDataSpn()) {
+ getStringExtra(EXTRA_SPN)
+ } else {
+ getStringExtra(EXTRA_DATA_SPN)
+ }
+
val showPlmn = getBooleanExtra(EXTRA_SHOW_PLMN, false)
val plmn = getStringExtra(EXTRA_PLMN)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index d5cd86ec0b76..c8cc6b5fdf93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -50,14 +50,18 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +77,8 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
private static final String TEST_PACKAGE = "test_package";
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
// Mock
private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
private MediaController mMediaController = mock(MediaController.class);
@@ -122,6 +128,9 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
mMediaControllers.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
mMediaOutputController =
new MediaOutputController(
@@ -139,6 +148,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ volumePanelGlobalStateInteractor,
mUserTracker);
// Using a fake package will cause routing operations to fail, so we intercept
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 87d224579e95..189a56145d27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -51,14 +51,18 @@ import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import com.google.common.base.Strings;
@@ -81,6 +85,8 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
private static final String BROADCAST_CODE_TEST = "112233";
private static final String BROADCAST_CODE_UPDATE_TEST = "11223344";
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
@@ -123,6 +129,9 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST);
when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn(
BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8));
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
mMediaOutputController =
new MediaOutputController(
@@ -140,6 +149,7 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ volumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 856bc0b78215..714fad9d7478 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -72,15 +72,19 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import com.google.common.collect.ImmutableList;
@@ -158,11 +162,16 @@ public class MediaOutputControllerTest extends SysuiTestCase {
@Mock
private UserTracker mUserTracker;
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
private FeatureFlags mFlags = mock(FeatureFlags.class);
private View mDialogLaunchView = mock(View.class);
private MediaOutputController.Callback mCallback = mock(MediaOutputController.Callback.class);
final Notification mNotification = mock(Notification.class);
+ private final VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
private Context mSpyContext;
private String mPackageName = null;
@@ -194,6 +203,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
mCachedBluetoothDeviceManager);
+
mMediaOutputController =
new MediaOutputController(
mSpyContext,
@@ -210,6 +220,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
@@ -304,6 +315,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.start(mCb);
@@ -346,6 +358,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.start(mCb);
@@ -602,6 +615,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -636,6 +650,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
testMediaOutputController.start(mCb);
reset(mCb);
@@ -683,6 +698,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
@@ -710,6 +726,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
@@ -990,6 +1007,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
@@ -1193,6 +1211,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ mVolumePanelGlobalStateInteractor,
mUserTracker);
testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index f20b04ae0e5c..90c2930f8e49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -54,14 +54,18 @@ import com.android.settingslib.flags.Flags;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
import org.junit.After;
import org.junit.Before;
@@ -84,6 +88,8 @@ public class MediaOutputDialogTest extends SysuiTestCase {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private MediaController mMediaController = mock(MediaController.class);
@@ -136,6 +142,9 @@ public class MediaOutputDialogTest extends SysuiTestCase {
when(mMediaSessionManager.getActiveSessionsForUser(any(),
Mockito.eq(userHandle))).thenReturn(
mMediaControllers);
+ VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+ VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+ mKosmos);
mMediaOutputController =
new MediaOutputController(
@@ -153,6 +162,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
mPowerExemptionManager,
mKeyguardManager,
mFlags,
+ volumePanelGlobalStateInteractor,
mUserTracker);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = makeTestDialog(mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 8fd0b31b82f3..171520f72269 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
@@ -59,6 +60,7 @@ import android.telephony.TelephonyManager.DATA_UNKNOWN
import android.telephony.TelephonyManager.ERI_OFF
import android.telephony.TelephonyManager.ERI_ON
import android.telephony.TelephonyManager.EXTRA_CARRIER_ID
+import android.telephony.TelephonyManager.EXTRA_DATA_SPN
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
@@ -69,6 +71,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.MobileMappings
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
@@ -85,7 +88,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionMod
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
@@ -93,8 +95,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.Mobil
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -112,6 +112,8 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@@ -807,6 +809,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_usesBroadcastInfo_returnsDerived() =
testScope.runTest {
var latest: NetworkNameModel? = null
@@ -815,14 +818,34 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ // spnIntent() sets all values to true and test strings
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
job.cancel()
}
@Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_usesBroadcastInfo_returnsDerived_flagOff() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
+
+ // spnIntent() sets all values to true and test strings
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+ job.cancel()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_broadcastNotForThisSubId_keepsOldValue() =
testScope.runTest {
var latest: NetworkNameModel? = null
@@ -831,22 +854,48 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
+
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+
+ // WHEN an intent with a different subId is sent
+ val wrongSubIntent = spnIntent(subId = 101)
+
+ captor.lastValue.onReceive(context, wrongSubIntent)
+
+ // THEN the previous intent's name is still used
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+
+ job.cancel()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_broadcastNotForThisSubId_keepsOldValue_flagOff() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
- captor.value!!.onReceive(context, wrongSubIntent)
+ captor.lastValue.onReceive(context, wrongSubIntent)
// THEN the previous intent's name is still used
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
job.cancel()
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_broadcastHasNoData_updatesToDefault() =
testScope.runTest {
var latest: NetworkNameModel? = null
@@ -855,9 +904,9 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
val intentWithoutInfo =
spnIntent(
@@ -865,7 +914,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
showPlmn = false,
)
- captor.value!!.onReceive(context, intentWithoutInfo)
+ captor.lastValue.onReceive(context, intentWithoutInfo)
assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
@@ -873,6 +922,34 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_broadcastHasNoData_updatesToDefault_flagOff() =
+ testScope.runTest {
+ var latest: NetworkNameModel? = null
+ val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
+
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+ val intentWithoutInfo =
+ spnIntent(
+ showSpn = false,
+ showPlmn = false,
+ )
+
+ captor.lastValue.onReceive(context, intentWithoutInfo)
+
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+
+ job.cancel()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers() =
testScope.runTest {
// Use the [StateFlow.value] getter so we can prove that the collection happens
@@ -884,10 +961,172 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
val intent = spnIntent()
val captor = argumentCaptor<BroadcastReceiver>()
verify(context).registerReceiver(captor.capture(), any())
- captor.value!!.onReceive(context, intent)
+ captor.lastValue.onReceive(context, intent)
+
+ // The value is still there despite no active subscribers
+ assertThat(underTest.networkName.value)
+ .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers_flagOff() =
+ testScope.runTest {
+ // Use the [StateFlow.value] getter so we can prove that the collection happens
+ // even when there is no [Job]
+
+ // Starts out default
+ assertThat(underTest.networkName.value).isEqualTo(DEFAULT_NAME_MODEL)
+
+ val intent = spnIntent()
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.lastValue.onReceive(context, intent)
// The value is still there despite no active subscribers
- assertThat(underTest.networkName.value).isEqualTo(intent.toNetworkNameModel(SEP))
+ assertThat(underTest.networkName.value)
+ .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_allFieldsSet_doesNotUseDataSpn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_allFieldsSet_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = null,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = null,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ fun networkName_showPlmn_noShowSPN() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = false,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$SPN"))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn_flagOff() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = DATA_SPN,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$DATA_SPN"))
}
@Test
@@ -1128,14 +1367,16 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
private fun spnIntent(
subId: Int = SUB_1_ID,
showSpn: Boolean = true,
- spn: String = SPN,
+ spn: String? = SPN,
+ dataSpn: String? = DATA_SPN,
showPlmn: Boolean = true,
- plmn: String = PLMN,
+ plmn: String? = PLMN,
): Intent =
Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
putExtra(EXTRA_SHOW_SPN, showSpn)
putExtra(EXTRA_SPN, spn)
+ putExtra(EXTRA_DATA_SPN, dataSpn)
putExtra(EXTRA_SHOW_PLMN, showPlmn)
putExtra(EXTRA_PLMN, plmn)
}
@@ -1148,6 +1389,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
private const val SEP = "-"
private const val SPN = "testSpn"
+ private const val DATA_SPN = "testDataSpn"
private const val PLMN = "testPlmn"
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index db48835a9b82..0f50260a55d2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -961,14 +961,17 @@ public class ZenModeHelper {
if (origin == ORIGIN_USER_IN_SYSTEMUI && condition != null
&& condition.source == SOURCE_USER_ACTION) {
// Apply as override, instead of actual condition.
+ // If the new override is the reverse of a previous (still active) override, try
+ // removing the previous override, as long as the resulting state, based on the
+ // previous owner-provided condition, is the desired one (active or inactive).
+ // This allows the rule owner to resume controlling the rule after
+ // snoozing-unsnoozing or activating-stopping.
if (condition.state == STATE_TRUE) {
- // Manually turn on a rule -> Apply override.
- rule.setConditionOverride(OVERRIDE_ACTIVATE);
+ rule.resetConditionOverride();
+ if (!rule.isAutomaticActive()) {
+ rule.setConditionOverride(OVERRIDE_ACTIVATE);
+ }
} else if (condition.state == STATE_FALSE) {
- // Manually turn off a rule. If the rule was manually activated before, reset
- // override -- but only if this will not result in the rule turning on
- // immediately because of a previously snoozed condition! In that case, apply
- // deactivate-override.
rule.resetConditionOverride();
if (rule.isAutomaticActive()) {
rule.setConditionOverride(OVERRIDE_DEACTIVATE);
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
index 48a9311c0374..b03af4c3734a 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -26,9 +26,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.RoundedCorner;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
@@ -44,12 +41,17 @@ class AppCompatLetterboxPolicy {
private final ActivityRecord mActivityRecord;
@NonNull
private final LetterboxPolicyState mLetterboxPolicyState;
+ @NonNull
+ private final AppCompatRoundedCorners mAppCompatRoundedCorners;
private boolean mLastShouldShowLetterboxUi;
AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord) {
mActivityRecord = activityRecord;
mLetterboxPolicyState = new LetterboxPolicyState();
+ // TODO (b/358334569) Improve cutout logic dependency on app compat.
+ mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord,
+ this::isLetterboxedNotForDisplayCutout);
}
/** Cleans up {@link Letterbox} if it exists.*/
@@ -105,7 +107,7 @@ class AppCompatLetterboxPolicy {
if (shouldNotLayoutLetterbox(w)) {
return;
}
- updateRoundedCornersIfNeeded(w);
+ mAppCompatRoundedCorners.updateRoundedCornersIfNeeded(w);
updateWallpaperForLetterbox(w);
if (shouldShowLetterboxUi(w)) {
mLetterboxPolicyState.layoutLetterboxIfNeeded(w);
@@ -138,94 +140,20 @@ class AppCompatLetterboxPolicy {
@VisibleForTesting
@Nullable
Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
- // We don't want corner radius on the window.
- // In the case the ActivityRecord requires a letterboxed animation we never want
- // rounded corners on the window because rounded corners are applied at the
- // animation-bounds surface level and rounded corners on the window would interfere
- // with that leading to unexpected rounded corner positioning during the animation.
- return null;
- }
-
- final Rect cropBounds = new Rect(mActivityRecord.getBounds());
-
- // In case of translucent activities we check if the requested size is different from
- // the size provided using inherited bounds. In that case we decide to not apply rounded
- // corners because we assume the specific layout would. This is the case when the layout
- // of the translucent activity uses only a part of all the bounds because of the use of
- // LayoutParams.WRAP_CONTENT.
- final TransparentPolicy transparentPolicy = mActivityRecord.mAppCompatController
- .getTransparentPolicy();
- if (transparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
- || cropBounds.height() != mainWindow.mRequestedHeight)) {
- return null;
- }
-
- // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
- // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
- // are in screen coordinates
- adjustBoundsForTaskbar(mainWindow, cropBounds);
-
- final float scale = mainWindow.mInvGlobalScale;
- if (scale != 1f && scale > 0f) {
- cropBounds.scale(scale);
- }
-
- // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
- // control is in the top left corner of an app window so offsetting bounds
- // accordingly.
- cropBounds.offsetTo(0, 0);
- return cropBounds;
+ return mAppCompatRoundedCorners.getCropBoundsIfNeeded(mainWindow);
}
-
- // Returns rounded corners radius the letterboxed activity should have based on override in
- // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
- // Device corners can be different on the right and left sides, but we use the same radius
- // for all corners for consistency and pick a minimal bottom one for consistency with a
- // taskbar rounded corners.
+ /**
+ * Returns rounded corners radius the letterboxed activity should have based on override in
+ * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+ * Device corners can be different on the right and left sides, but we use the same radius
+ * for all corners for consistency and pick a minimal bottom one for consistency with a
+ * taskbar rounded corners.
+ *
+ * @param mainWindow The {@link WindowState} to consider for the rounded corners calculation.
+ */
int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
- if (!requiresRoundedCorners(mainWindow)) {
- return 0;
- }
- final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
- .mAppCompatController.getAppCompatLetterboxOverrides();
- final int radius;
- if (letterboxOverrides.getLetterboxActivityCornersRadius() >= 0) {
- radius = letterboxOverrides.getLetterboxActivityCornersRadius();
- } else {
- final InsetsState insetsState = mainWindow.getInsetsState();
- radius = Math.min(
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
- getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
- }
-
- final float scale = mainWindow.mInvGlobalScale;
- return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
- }
-
- void adjustBoundsForTaskbar(@NonNull final WindowState mainWindow,
- @NonNull final Rect bounds) {
- // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
- // an insets frame is equal to a navigation bar which shouldn't affect position of
- // rounded corners since apps are expected to handle navigation bar inset.
- // This condition checks whether the taskbar is visible.
- // Do not crop the taskbar inset if the window is in immersive mode - the user can
- // swipe to show/hide the taskbar as an overlay.
- // Adjust the bounds only in case there is an expanded taskbar,
- // otherwise the rounded corners will be shown behind the navbar.
- final InsetsSource expandedTaskbarOrNull =
- AppCompatUtils.getExpandedTaskbarOrNull(mainWindow);
- if (expandedTaskbarOrNull != null) {
- // Rounded corners should be displayed above the expanded taskbar.
- bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
- }
- }
-
- private int getInsetsStateCornerRadius(@NonNull InsetsState insetsState,
- @RoundedCorner.Position int position) {
- final RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
- return corner == null ? 0 : corner.getRadius();
+ return mAppCompatRoundedCorners.getRoundedCornersRadius(mainWindow);
}
private void updateWallpaperForLetterbox(@NonNull WindowState mainWindow) {
@@ -248,25 +176,6 @@ class AppCompatLetterboxPolicy {
}
}
- void updateRoundedCornersIfNeeded(@NonNull final WindowState mainWindow) {
- final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
- if (windowSurface == null || !windowSurface.isValid()) {
- return;
- }
-
- // cropBounds must be non-null for the cornerRadius to be ever applied.
- mActivityRecord.getSyncTransaction()
- .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
- .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
- }
-
- private boolean requiresRoundedCorners(@NonNull final WindowState mainWindow) {
- final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
- .mAppCompatController.getAppCompatLetterboxOverrides();
- return isLetterboxedNotForDisplayCutout(mainWindow)
- && letterboxOverrides.isLetterboxActivityCornersRounded();
- }
-
private boolean isLetterboxedNotForDisplayCutout(@NonNull WindowState mainWindow) {
return shouldShowLetterboxUi(mainWindow)
&& !mainWindow.isLetterboxedForDisplayCutout();
@@ -405,7 +314,7 @@ class AppCompatLetterboxPolicy {
outBounds.set(mLetterbox.getInnerFrame());
final WindowState w = mActivityRecord.findMainWindow();
if (w != null) {
- adjustBoundsForTaskbar(w, outBounds);
+ AppCompatUtils.adjustBoundsForTaskbar(w, outBounds);
}
} else {
outBounds.setEmpty();
diff --git a/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
new file mode 100644
index 000000000000..077ce00c6033
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Predicate;
+
+/**
+ * Utility to unify rounded corners in app compat.
+ */
+class AppCompatRoundedCorners {
+
+ @NonNull
+ private final ActivityRecord mActivityRecord;
+ @NonNull
+ private final Predicate<WindowState> mIsLetterboxedNotForDisplayCutout;
+
+ AppCompatRoundedCorners(@NonNull ActivityRecord activityRecord,
+ @NonNull Predicate<WindowState> isLetterboxedNotForDisplayCutout) {
+ mActivityRecord = activityRecord;
+ mIsLetterboxedNotForDisplayCutout = isLetterboxedNotForDisplayCutout;
+ }
+
+ void updateRoundedCornersIfNeeded(@NonNull final WindowState mainWindow) {
+ final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
+ if (windowSurface == null || !windowSurface.isValid()) {
+ return;
+ }
+
+ // cropBounds must be non-null for the cornerRadius to be ever applied.
+ mActivityRecord.getSyncTransaction()
+ .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
+ .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+ }
+
+ @VisibleForTesting
+ @Nullable
+ Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+ // We don't want corner radius on the window.
+ // In the case the ActivityRecord requires a letterboxed animation we never want
+ // rounded corners on the window because rounded corners are applied at the
+ // animation-bounds surface level and rounded corners on the window would interfere
+ // with that leading to unexpected rounded corner positioning during the animation.
+ return null;
+ }
+
+ final Rect cropBounds = new Rect(mActivityRecord.getBounds());
+
+ // In case of translucent activities we check if the requested size is different from
+ // the size provided using inherited bounds. In that case we decide to not apply rounded
+ // corners because we assume the specific layout would. This is the case when the layout
+ // of the translucent activity uses only a part of all the bounds because of the use of
+ // LayoutParams.WRAP_CONTENT.
+ final TransparentPolicy transparentPolicy = mActivityRecord.mAppCompatController
+ .getTransparentPolicy();
+ if (transparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
+ || cropBounds.height() != mainWindow.mRequestedHeight)) {
+ return null;
+ }
+
+ // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
+ // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
+ // are in screen coordinates
+ AppCompatUtils.adjustBoundsForTaskbar(mainWindow, cropBounds);
+
+ final float scale = mainWindow.mInvGlobalScale;
+ if (scale != 1f && scale > 0f) {
+ cropBounds.scale(scale);
+ }
+
+ // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
+ // control is in the top left corner of an app window so offsetting bounds
+ // accordingly.
+ cropBounds.offsetTo(0, 0);
+ return cropBounds;
+ }
+
+ /**
+ * Returns rounded corners radius the letterboxed activity should have based on override in
+ * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+ * Device corners can be different on the right and left sides, but we use the same radius
+ * for all corners for consistency and pick a minimal bottom one for consistency with a
+ * taskbar rounded corners.
+ *
+ * @param mainWindow The {@link WindowState} to consider for rounded corners calculation.
+ */
+ int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
+ if (!requiresRoundedCorners(mainWindow)) {
+ return 0;
+ }
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ final int radius;
+ if (letterboxOverrides.getLetterboxActivityCornersRadius() >= 0) {
+ radius = letterboxOverrides.getLetterboxActivityCornersRadius();
+ } else {
+ final InsetsState insetsState = mainWindow.getInsetsState();
+ radius = Math.min(
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+ getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+ }
+
+ final float scale = mainWindow.mInvGlobalScale;
+ return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
+ }
+
+ private static int getInsetsStateCornerRadius(@NonNull InsetsState insetsState,
+ @RoundedCorner.Position int position) {
+ final RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
+ return corner == null ? 0 : corner.getRadius();
+ }
+
+ private boolean requiresRoundedCorners(@NonNull final WindowState mainWindow) {
+ final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+ .mAppCompatController.getAppCompatLetterboxOverrides();
+ return mIsLetterboxedNotForDisplayCutout.test(mainWindow)
+ && letterboxOverrides.isLetterboxActivityCornersRounded();
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 91205fc757ad..2b842d99a3ab 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -37,7 +37,7 @@ import java.util.function.BooleanSupplier;
/**
* Utilities for App Compat policies and overrides.
*/
-class AppCompatUtils {
+final class AppCompatUtils {
/**
* Lazy version of a {@link BooleanSupplier} which access an existing BooleanSupplier and
@@ -232,6 +232,23 @@ class AppCompatUtils {
return null;
}
+ static void adjustBoundsForTaskbar(@NonNull final WindowState mainWindow,
+ @NonNull final Rect bounds) {
+ // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
+ // an insets frame is equal to a navigation bar which shouldn't affect position of
+ // rounded corners since apps are expected to handle navigation bar inset.
+ // This condition checks whether the taskbar is visible.
+ // Do not crop the taskbar inset if the window is in immersive mode - the user can
+ // swipe to show/hide the taskbar as an overlay.
+ // Adjust the bounds only in case there is an expanded taskbar,
+ // otherwise the rounded corners will be shown behind the navbar.
+ final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
+ if (expandedTaskbarOrNull != null) {
+ // Rounded corners should be displayed above the expanded taskbar.
+ bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
+ }
+ }
+
private static void clearAppCompatTaskInfo(@NonNull AppCompatTaskInfo info) {
info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index f1e94deb8a45..a5085fc3147c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -211,7 +211,6 @@ cc_defaults {
"android.system.suspend-V1-ndk",
"server_configurable_flags",
"service.incremental",
- "android.companion.virtualdevice.flags-aconfig-cc",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 9ec5ae5a8e51..6f7276079970 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -19,273 +19,22 @@
#include <android-base/unique_fd.h>
#include <android/input.h>
#include <android/keycodes.h>
-#include <android_companion_virtualdevice_flags.h>
-#include <errno.h>
-#include <fcntl.h>
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
-#include <linux/uinput.h>
-#include <math.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
-#include <map>
-#include <set>
#include <string>
using android::base::unique_fd;
namespace android {
-namespace vd_flags = android::companion::virtualdevice::flags;
-
static constexpr jlong INVALID_PTR = 0;
-enum class DeviceType {
- KEYBOARD,
- MOUSE,
- TOUCHSCREEN,
- DPAD,
- STYLUS,
- ROTARY_ENCODER,
-};
-
-static unique_fd invalidFd() {
- return unique_fd(-1);
-}
-
-/** Creates a new uinput device and assigns a file descriptor. */
-static unique_fd openUinput(const char* readableName, jint vendorId, jint productId,
- const char* phys, DeviceType deviceType, jint screenHeight,
- jint screenWidth) {
- unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
- if (fd < 0) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
-
- ioctl(fd, UI_SET_PHYS, phys);
-
- ioctl(fd, UI_SET_EVBIT, EV_KEY);
- ioctl(fd, UI_SET_EVBIT, EV_SYN);
- switch (deviceType) {
- case DeviceType::DPAD:
- for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
- ioctl(fd, UI_SET_KEYBIT, keyCode);
- }
- break;
- case DeviceType::KEYBOARD:
- for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
- ioctl(fd, UI_SET_KEYBIT, keyCode);
- }
- break;
- case DeviceType::MOUSE:
- ioctl(fd, UI_SET_EVBIT, EV_REL);
- ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
- ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
- ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
- ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
- ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
- ioctl(fd, UI_SET_RELBIT, REL_X);
- ioctl(fd, UI_SET_RELBIT, REL_Y);
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
- ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
- if (vd_flags::high_resolution_scroll()) {
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
- ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
- }
- break;
- case DeviceType::TOUCHSCREEN:
- ioctl(fd, UI_SET_EVBIT, EV_ABS);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
- ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
- ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
- break;
- case DeviceType::STYLUS:
- ioctl(fd, UI_SET_EVBIT, EV_ABS);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
- ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
- ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
- ioctl(fd, UI_SET_ABSBIT, ABS_X);
- ioctl(fd, UI_SET_ABSBIT, ABS_Y);
- ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
- ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
- ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
- ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
- break;
- case DeviceType::ROTARY_ENCODER:
- ioctl(fd, UI_SET_EVBIT, EV_REL);
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
- if (vd_flags::high_resolution_scroll()) {
- ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
- }
- break;
- default:
- ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
- return invalidFd();
- }
-
- int version;
- if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
- uinput_setup setup;
- memset(&setup, 0, sizeof(setup));
- strlcpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
- setup.id.version = 1;
- setup.id.bustype = BUS_VIRTUAL;
- setup.id.vendor = vendorId;
- setup.id.product = productId;
- if (deviceType == DeviceType::TOUCHSCREEN) {
- uinput_abs_setup xAbsSetup;
- xAbsSetup.code = ABS_MT_POSITION_X;
- xAbsSetup.absinfo.maximum = screenWidth - 1;
- xAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup yAbsSetup;
- yAbsSetup.code = ABS_MT_POSITION_Y;
- yAbsSetup.absinfo.maximum = screenHeight - 1;
- yAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup majorAbsSetup;
- majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
- majorAbsSetup.absinfo.maximum = screenWidth - 1;
- majorAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup pressureAbsSetup;
- pressureAbsSetup.code = ABS_MT_PRESSURE;
- pressureAbsSetup.absinfo.maximum = 255;
- pressureAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup slotAbsSetup;
- slotAbsSetup.code = ABS_MT_SLOT;
- slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
- slotAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup trackingIdAbsSetup;
- trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
- trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
- trackingIdAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
- return invalidFd();
- }
- } else if (deviceType == DeviceType::STYLUS) {
- uinput_abs_setup xAbsSetup;
- xAbsSetup.code = ABS_X;
- xAbsSetup.absinfo.maximum = screenWidth - 1;
- xAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup yAbsSetup;
- yAbsSetup.code = ABS_Y;
- yAbsSetup.absinfo.maximum = screenHeight - 1;
- yAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup tiltXAbsSetup;
- tiltXAbsSetup.code = ABS_TILT_X;
- tiltXAbsSetup.absinfo.maximum = 90;
- tiltXAbsSetup.absinfo.minimum = -90;
- if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup tiltYAbsSetup;
- tiltYAbsSetup.code = ABS_TILT_Y;
- tiltYAbsSetup.absinfo.maximum = 90;
- tiltYAbsSetup.absinfo.minimum = -90;
- if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
- ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
- return invalidFd();
- }
- uinput_abs_setup pressureAbsSetup;
- pressureAbsSetup.code = ABS_PRESSURE;
- pressureAbsSetup.absinfo.maximum = 255;
- pressureAbsSetup.absinfo.minimum = 0;
- if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
- ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
- return invalidFd();
- }
- }
- if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
- } else {
- // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
- ALOGI("Falling back to version %d manual setup", version);
- uinput_user_dev fallback;
- memset(&fallback, 0, sizeof(fallback));
- strlcpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
- fallback.id.version = 1;
- fallback.id.bustype = BUS_VIRTUAL;
- fallback.id.vendor = vendorId;
- fallback.id.product = productId;
- if (deviceType == DeviceType::TOUCHSCREEN) {
- fallback.absmin[ABS_MT_POSITION_X] = 0;
- fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
- fallback.absmin[ABS_MT_POSITION_Y] = 0;
- fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
- fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
- fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
- fallback.absmin[ABS_MT_PRESSURE] = 0;
- fallback.absmax[ABS_MT_PRESSURE] = 255;
- } else if (deviceType == DeviceType::STYLUS) {
- fallback.absmin[ABS_X] = 0;
- fallback.absmax[ABS_X] = screenWidth - 1;
- fallback.absmin[ABS_Y] = 0;
- fallback.absmax[ABS_Y] = screenHeight - 1;
- fallback.absmin[ABS_TILT_X] = -90;
- fallback.absmax[ABS_TILT_X] = 90;
- fallback.absmin[ABS_TILT_Y] = -90;
- fallback.absmax[ABS_TILT_Y] = 90;
- fallback.absmin[ABS_PRESSURE] = 0;
- fallback.absmax[ABS_PRESSURE] = 255;
- }
- if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
- }
-
- if (ioctl(fd, UI_DEV_CREATE) != 0) {
- ALOGE("Error creating uinput device: %s", strerror(errno));
- return invalidFd();
- }
-
- return fd;
-}
-
static unique_fd openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
- jstring phys, DeviceType deviceType, int screenHeight,
- int screenWidth) {
+ jstring phys, DeviceType deviceType, jint screenHeight,
+ jint screenWidth) {
ScopedUtfChars readableName(env, name);
ScopedUtfChars readablePhys(env, phys);
return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 212e61e10448..ed8ebc842be8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -4972,7 +4972,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.setAutomaticZenRuleState(createdId,
new Condition(zenRule.getConditionId(), "", STATE_FALSE),
- ORIGIN_APP, SYSTEM_UID);
+ ORIGIN_APP, CUSTOM_PKG_UID);
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
@@ -6491,6 +6491,8 @@ public class ZenModeHelperTest extends UiServiceTestCase {
.build();
String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
CUSTOM_PKG_UID);
+ Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
+ SOURCE_CONTEXT);
ZenRule zenRule;
mZenModeHelper.setAutomaticZenRuleState(ruleId,
@@ -6508,6 +6510,57 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertThat(zenRule.isAutomaticActive()).isFalse();
assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
assertThat(zenRule.condition).isNull();
+
+ // Bonus check: app has resumed control over the rule and can now turn it on.
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn, ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+ }
+
+ @Test
+ @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+ public void setAutomaticZenRuleState_manualDeactivationAndThenReactivation_removesOverride() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+ .setPackage(mPkg)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+ CUSTOM_PKG_UID);
+ Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
+ SOURCE_CONTEXT);
+ Condition autoOff = new Condition(rule.getConditionId(), "auto-off", STATE_FALSE,
+ SOURCE_CONTEXT);
+ ZenRule zenRule;
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn, ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+ ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isTrue();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOn);
+
+ // Bonus check: app has resumed control over the rule and can now turn it off.
+ mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOff, ORIGIN_APP, CUSTOM_PKG_UID);
+ zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+ assertThat(zenRule.isAutomaticActive()).isFalse();
+ assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+ assertThat(zenRule.condition).isEqualTo(autoOff);
}
@Test
@@ -6521,7 +6574,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.setAutomaticZenRuleState(ruleId,
new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
- ORIGIN_APP, SYSTEM_UID);
+ ORIGIN_APP, CUSTOM_PKG_UID);
ZenRule zenRuleOn = mZenModeHelper.mConfig.automaticRules.get(ruleId);
assertThat(zenRuleOn.isAutomaticActive()).isTrue();
assertThat(zenRuleOn.getConditionOverride()).isEqualTo(OVERRIDE_NONE);