Merge "Fix size and color of priority modes icons in status bar" into main
diff --git a/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java b/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java
index 92dd9be..f4759b8 100644
--- a/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java
+++ b/apct-tests/perftests/protolog/src/com/android/internal/protolog/ProtoLogPerfTest.java
@@ -15,43 +15,54 @@
*/
package com.android.internal.protolog;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import android.app.Activity;
+import android.os.Bundle;
+import android.perftests.utils.Stats;
+
+import androidx.test.InstrumentationRegistry;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogLevel;
import org.junit.Before;
import org.junit.BeforeClass;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
@RunWith(Parameterized.class)
public class ProtoLogPerfTest {
- @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- @Parameters(name="logToProto_{0}_logToLogcat_{1}")
- public static Collection<Object[]> params() {
- return Arrays.asList(new Object[][] {
- { true, true },
- { true, false },
- { false, true },
- { false, false }
- });
- }
-
private final boolean mLogToProto;
private final boolean mLogToLogcat;
- public ProtoLogPerfTest(boolean logToProto, boolean logToLogcat) {
- mLogToProto = logToProto;
- mLogToLogcat = logToLogcat;
+ /**
+ * Generates the parameters used for this test class
+ */
+ @Parameters(name = "LOG_TO_{0}")
+ public static Collection<Object[]> params() {
+ var params = new ArrayList<Object[]>();
+
+ for (LogTo logTo : LogTo.values()) {
+ params.add(new Object[] { logTo });
+ }
+
+ return params;
+ }
+
+ public ProtoLogPerfTest(LogTo logTo) {
+ mLogToProto = switch (logTo) {
+ case ALL, PROTO_ONLY -> true;
+ case LOGCAT_ONLY, NONE -> false;
+ };
+
+ mLogToLogcat = switch (logTo) {
+ case ALL, LOGCAT_ONLY -> true;
+ case PROTO_ONLY, NONE -> false;
+ };
}
@BeforeClass
@@ -66,11 +77,11 @@
}
@Test
- public void logProcessedProtoLogMessageWithoutArgs() {
+ public void log_Processed_NoArgs() {
final var protoLog = ProtoLog.getSingleInstance();
+ final var perfMonitor = new PerfMonitor();
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ while (perfMonitor.keepRunning()) {
protoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123,
0, (Object[]) null);
@@ -78,11 +89,11 @@
}
@Test
- public void logProcessedProtoLogMessageWithArgs() {
+ public void log_Processed_WithArgs() {
final var protoLog = ProtoLog.getSingleInstance();
+ final var perfMonitor = new PerfMonitor();
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ while (perfMonitor.keepRunning()) {
protoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123,
0b1110101001010100,
@@ -91,18 +102,58 @@
}
@Test
- public void logNonProcessedProtoLogMessageWithNoArgs() {
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
+ public void log_Unprocessed_NoArgs() {
+ final var perfMonitor = new PerfMonitor();
+
+ while (perfMonitor.keepRunning()) {
ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message");
}
}
@Test
- public void logNonProcessedProtoLogMessageWithArgs() {
- BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test messag %s, %d, %b", "arg1", 2, true);
+ public void log_Unprocessed_WithArgs() {
+ final var perfMonitor = new PerfMonitor();
+
+ while (perfMonitor.keepRunning()) {
+ ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message %s, %d, %b", "arg1", 2, true);
+ }
+ }
+
+ private class PerfMonitor {
+ private int mIteration = 0;
+
+ private static final int WARM_UP_ITERATIONS = 10;
+ private static final int ITERATIONS = 1000;
+
+ private final ArrayList<Long> mResults = new ArrayList<>();
+
+ private long mStartTimeNs;
+
+ public boolean keepRunning() {
+ final long currentTime = System.nanoTime();
+
+ mIteration++;
+
+ if (mIteration > ITERATIONS) {
+ reportResults();
+ return false;
+ }
+
+ if (mIteration > WARM_UP_ITERATIONS) {
+ mResults.add(currentTime - mStartTimeNs);
+ }
+
+ mStartTimeNs = System.nanoTime();
+ return true;
+ }
+
+ public void reportResults() {
+ final var stats = new Stats(mResults);
+
+ Bundle status = new Bundle();
+ status.putLong("protologging_time_mean_ns", (long) stats.getMean());
+ status.putLong("protologging_time_median_ns", (long) stats.getMedian());
+ InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
}
}
@@ -168,4 +219,11 @@
return ordinal();
}
}
+
+ private enum LogTo {
+ PROTO_ONLY,
+ LOGCAT_ONLY,
+ ALL,
+ NONE,
+ }
}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 613b784..e5389b4 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -65,3 +65,13 @@
description: "Remove started user if user will be stopped due to user switch"
bug: "321598070"
}
+
+flag {
+ name: "use_correct_process_state_for_logging"
+ namespace: "backstage_power"
+ description: "Use correct process state for statsd logging"
+ bug: "361308212"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d65a66c..be8e304 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -473,6 +473,14 @@
mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid());
mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid());
+ int procState = mService.getUidProcState(job.getUid());
+ if (Flags.useCorrectProcessStateForLogging()
+ && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ // Try to get the latest proc state from AMS, there might be some delay
+ // for the proc states worse than TRANSIENT_BACKGROUND.
+ procState = mActivityManagerInternal.getUidProcessState(job.getUid());
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid},
// Given that the source tag is set by the calling app, it should be connected
@@ -517,7 +525,7 @@
job.getEstimatedNetworkDownloadBytes(),
job.getEstimatedNetworkUploadBytes(),
job.getWorkCount(),
- ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
+ ActivityManager.processStateAmToProto(procState),
job.getNamespaceHash(),
/* system_measured_source_download_bytes */ 0,
/* system_measured_source_upload_bytes */ 0,
@@ -1528,6 +1536,13 @@
mJobPackageTracker.noteInactive(completedJob,
loggingInternalStopReason, loggingDebugReason);
final int sourceUid = completedJob.getSourceUid();
+ int procState = mService.getUidProcState(completedJob.getUid());
+ if (Flags.useCorrectProcessStateForLogging()
+ && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ // Try to get the latest proc state from AMS, there might be some delay
+ // for the proc states worse than TRANSIENT_BACKGROUND.
+ procState = mActivityManagerInternal.getUidProcessState(completedJob.getUid());
+ }
FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
completedJob.isProxyJob()
? new int[]{sourceUid, completedJob.getUid()} : new int[]{sourceUid},
@@ -1573,7 +1588,7 @@
completedJob.getEstimatedNetworkUploadBytes(),
completedJob.getWorkCount(),
ActivityManager
- .processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
+ .processStateAmToProto(procState),
completedJob.getNamespaceHash(),
TrafficStats.getUidRxBytes(completedJob.getSourceUid())
- mInitialDownloadedBytesFromSource,
diff --git a/core/api/current.txt b/core/api/current.txt
index 2565d6f..ed8ab06 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -141,7 +141,7 @@
field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL";
field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS";
field public static final String MANAGE_DEVICE_POLICY_APP_USER_DATA = "android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA";
- field @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT";
+ field public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT";
field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT";
field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL";
field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
@@ -169,7 +169,7 @@
field public static final String MANAGE_DEVICE_POLICY_LOCK = "android.permission.MANAGE_DEVICE_POLICY_LOCK";
field public static final String MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS = "android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS";
field public static final String MANAGE_DEVICE_POLICY_LOCK_TASK = "android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK";
- field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
+ field public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA";
field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE";
field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
@@ -8081,7 +8081,7 @@
method public CharSequence getStartUserSessionMessage(@NonNull android.content.ComponentName);
method @Deprecated public boolean getStorageEncryption(@Nullable android.content.ComponentName);
method public int getStorageEncryptionStatus();
- method @FlaggedApi("android.app.admin.flags.esim_management_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) public java.util.Set<java.lang.Integer> getSubscriptionIds();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle();
method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
@@ -34116,7 +34116,7 @@
field public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
field public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
field public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
- field @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
+ field public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
field public static final String DISALLOW_AUTOFILL = "no_autofill";
field public static final String DISALLOW_BLUETOOTH = "no_bluetooth";
field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
@@ -34166,7 +34166,7 @@
field public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
field public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi";
- field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally";
+ field public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally";
field public static final String DISALLOW_SMS = "no_sms";
field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0fc77f0..d31d8f2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -56,7 +56,6 @@
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
@@ -17742,7 +17741,6 @@
* @throws SecurityException if the caller is not authorized to call this method.
* @return ids of all managed subscriptions currently downloaded by an admin on the device.
*/
- @FlaggedApi(FLAG_ESIM_MANAGEMENT_ENABLED)
@RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
@NonNull
public Set<Integer> getSubscriptionIds() {
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index f2861fe..29a5048 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -139,6 +139,7 @@
bug: "293441361"
}
+# Fully rolled out and must not be used.
flag {
name: "assist_content_user_restriction_enabled"
is_exported: true
@@ -165,6 +166,7 @@
bug: "304999634"
}
+# Fully rolled out and must not be used.
flag {
name: "esim_management_enabled"
is_exported: true
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
new file mode 100644
index 0000000..3169f0e
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID;
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_ENABLED;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_INDEXER_PACKAGE;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.STATIC_PROPERTY_ENABLED_BY_DEFAULT;
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.JoinSpec;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+import android.app.appsearch.SearchSpec;
+import android.os.OutcomeReceiver;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class containing utilities for {@link AppFunctionManager}.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionManagerHelper {
+
+ /**
+ * Returns (through a callback) a boolean indicating whether the app function is enabled.
+ * <p>
+ * This method can only check app functions that are owned by the caller owned by packages
+ * visible to the caller.
+ * <p>
+ * If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
+ * <ul>
+ * <li>{@link IllegalArgumentException}, if the function is not found</li>
+ * <li>{@link SecurityException}, if the caller does not have permission to query the
+ * target package
+ * </li>
+ * </ul>
+ *
+ * @param functionIdentifier the identifier of the app function to check (unique within the
+ * target package) and in most cases, these are automatically
+ * generated by the AppFunctions SDK
+ * @param targetPackage the package name of the app function's owner
+ * @param appSearchExecutor the executor to run the metadata search mechanism through AppSearch
+ * @param callbackExecutor the executor to run the callback
+ * @param callback the callback to receive the function enabled check result
+ * @hide
+ */
+ public static void isAppFunctionEnabled(
+ @NonNull String functionIdentifier,
+ @NonNull String targetPackage,
+ @NonNull AppSearchManager appSearchManager,
+ @NonNull Executor appSearchExecutor,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Boolean, Exception> callback
+ ) {
+ Objects.requireNonNull(functionIdentifier);
+ Objects.requireNonNull(targetPackage);
+ Objects.requireNonNull(appSearchManager);
+ Objects.requireNonNull(appSearchExecutor);
+ Objects.requireNonNull(callbackExecutor);
+ Objects.requireNonNull(callback);
+
+ appSearchManager.createGlobalSearchSession(appSearchExecutor,
+ (searchSessionResult) -> {
+ if (!searchSessionResult.isSuccess()) {
+ callbackExecutor.execute(() ->
+ callback.onError(failedResultToException(searchSessionResult)));
+ return;
+ }
+ try (GlobalSearchSession searchSession = searchSessionResult.getResultValue()) {
+ SearchResults results = searchJoinedStaticWithRuntimeAppFunctions(
+ searchSession, targetPackage, functionIdentifier);
+ results.getNextPage(appSearchExecutor,
+ listAppSearchResult -> callbackExecutor.execute(() -> {
+ if (listAppSearchResult.isSuccess()) {
+ callback.onResult(getEnabledStateFromSearchResults(
+ Objects.requireNonNull(
+ listAppSearchResult.getResultValue())));
+ } else {
+ callback.onError(
+ failedResultToException(listAppSearchResult));
+ }
+ }));
+ } catch (Exception e) {
+ callbackExecutor.execute(() -> callback.onError(e));
+ }
+ });
+ }
+
+ /**
+ * Searches joined app function static and runtime metadata using the function Id and the
+ * package.
+ *
+ * @hide
+ */
+ private static @NonNull SearchResults searchJoinedStaticWithRuntimeAppFunctions(
+ @NonNull GlobalSearchSession session,
+ @NonNull String targetPackage,
+ @NonNull String functionIdentifier) {
+ SearchSpec runtimeSearchSpec = getAppFunctionRuntimeMetadataSearchSpecByFunctionId(
+ targetPackage);
+ JoinSpec joinSpec = new JoinSpec.Builder(
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+ .setNestedSearch(
+ functionIdentifier,
+ runtimeSearchSpec).build();
+ SearchSpec joinedStaticWithRuntimeSearchSpec = new SearchSpec.Builder()
+ .setJoinSpec(joinSpec)
+ .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+ .addFilterSchemas(
+ AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
+ targetPackage))
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
+ return session.search(functionIdentifier, joinedStaticWithRuntimeSearchSpec);
+ }
+
+ /**
+ * Finds whether the function is enabled or not from the search results returned by
+ * {@link #searchJoinedStaticWithRuntimeAppFunctions}.
+ *
+ * @throws IllegalArgumentException if the function is not found in the results
+ * @hide
+ */
+ private static boolean getEnabledStateFromSearchResults(
+ @NonNull List<SearchResult> joinedStaticRuntimeResults) {
+ if (joinedStaticRuntimeResults.isEmpty()) {
+ // Function not found.
+ throw new IllegalArgumentException("App function not found.");
+ } else {
+ List<SearchResult> runtimeMetadataResults =
+ joinedStaticRuntimeResults.getFirst().getJoinedResults();
+ if (!runtimeMetadataResults.isEmpty()) {
+ Boolean result = (Boolean) runtimeMetadataResults
+ .getFirst().getGenericDocument()
+ .getProperty(PROPERTY_ENABLED);
+ if (result != null) {
+ return result;
+ }
+ }
+ // Runtime metadata not found. Using the default value in the static metadata.
+ return joinedStaticRuntimeResults.getFirst().getGenericDocument()
+ .getPropertyBoolean(STATIC_PROPERTY_ENABLED_BY_DEFAULT);
+ }
+ }
+
+ /**
+ * Returns search spec that queries app function metadata for a specific package name by its
+ * function identifier.
+ *
+ * @hide
+ */
+ public static @NonNull SearchSpec getAppFunctionRuntimeMetadataSearchSpecByFunctionId(
+ @NonNull String targetPackage) {
+ return new SearchSpec.Builder()
+ .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+ .addFilterSchemas(
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(
+ targetPackage))
+ .addFilterProperties(
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(
+ targetPackage),
+ List.of(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID))
+ .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+ .build();
+ }
+
+ /**
+ * Converts a failed app search result codes into an exception.
+ *
+ * @hide
+ */
+ public static @NonNull Exception failedResultToException(
+ @NonNull AppSearchResult appSearchResult) {
+ return switch (appSearchResult.getResultCode()) {
+ case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException(
+ appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR -> new IOException(
+ appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException(
+ appSearchResult.getErrorMessage());
+ default -> new IllegalStateException(appSearchResult.getErrorMessage());
+ };
+ }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
new file mode 100644
index 0000000..fdd12b0
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Represents runtime function metadata of an app function.
+ *
+ * <p>This is a temporary solution for app function indexing, as later we would like to index the
+ * actual function signature entity class shape instead of just the schema info.
+ *
+ * @hide
+ */
+// TODO(b/357551503): Link to canonical docs rather than duplicating once they
+// are available.
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionRuntimeMetadata extends GenericDocument {
+ public static final String RUNTIME_SCHEMA_TYPE = "AppFunctionRuntimeMetadata";
+ public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
+ public static final String APP_FUNCTION_RUNTIME_METADATA_DB = "appfunctions-db";
+ public static final String APP_FUNCTION_RUNTIME_NAMESPACE = "app_functions_runtime";
+ public static final String PROPERTY_FUNCTION_ID = "functionId";
+ public static final String PROPERTY_PACKAGE_NAME = "packageName";
+ public static final String PROPERTY_ENABLED = "enabled";
+ public static final String PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID =
+ "appFunctionStaticMetadataQualifiedId";
+ private static final String TAG = "AppSearchAppFunction";
+ private static final String RUNTIME_SCHEMA_TYPE_SEPARATOR = "-";
+
+ public AppFunctionRuntimeMetadata(@NonNull GenericDocument genericDocument) {
+ super(genericDocument);
+ }
+
+ /**
+ * Returns a per-app runtime metadata schema name, to store all functions for that package.
+ */
+ public static String getRuntimeSchemaNameForPackage(@NonNull String pkg) {
+ return RUNTIME_SCHEMA_TYPE + RUNTIME_SCHEMA_TYPE_SEPARATOR + Objects.requireNonNull(pkg);
+ }
+
+ /**
+ * Returns the document id for an app function's runtime metadata.
+ */
+ public static String getDocumentIdForAppFunction(
+ @NonNull String pkg, @NonNull String functionId) {
+ return pkg + "/" + functionId;
+ }
+
+ /**
+ * Different packages have different visibility requirements. To allow for different visibility,
+ * we need to have per-package app function schemas.
+ * <p>This schema should be set visible to callers from the package owner itself and for callers
+ * with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+ *
+ * @param packageName The package name to create a schema for.
+ */
+ @NonNull
+ public static AppSearchSchema createAppFunctionRuntimeSchema(@NonNull String packageName) {
+ return new AppSearchSchema.Builder(getRuntimeSchemaNameForPackage(packageName))
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_FUNCTION_ID)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_VERBATIM)
+ .build())
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_PACKAGE_NAME)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_VERBATIM)
+ .build())
+ .addProperty(
+ new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder(
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setJoinableValueType(
+ AppSearchSchema.StringPropertyConfig
+ .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+ .build())
+ .addParentType(RUNTIME_SCHEMA_TYPE)
+ .build();
+ }
+
+ /**
+ * Returns the function id. This might look like "com.example.message#send_message".
+ */
+ @NonNull
+ public String getFunctionId() {
+ return Objects.requireNonNull(getPropertyString(PROPERTY_FUNCTION_ID));
+ }
+
+ /**
+ * Returns the package name of the package that owns this function.
+ */
+ @NonNull
+ public String getPackageName() {
+ return Objects.requireNonNull(getPropertyString(PROPERTY_PACKAGE_NAME));
+ }
+
+ /**
+ * Returns if the function is set to be enabled or not. If not set, the {@link
+ * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT} value would be used.
+ */
+ @Nullable
+ public Boolean getEnabled() {
+ return (Boolean) getProperty(PROPERTY_ENABLED);
+ }
+
+ /**
+ * Returns the qualified id linking to the static metadata of the app function.
+ */
+ @Nullable
+ @VisibleForTesting
+ public String getAppFunctionStaticMetadataQualifiedId() {
+ return getPropertyString(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID);
+ }
+
+ public static final class Builder extends GenericDocument.Builder<Builder> {
+ /**
+ * Creates a Builder for a {@link AppFunctionRuntimeMetadata}.
+ *
+ * @param packageName the name of the package that owns the function.
+ * @param functionId the id of the function.
+ * @param staticMetadataQualifiedId the qualified static metadata id that this runtime
+ * metadata refers to.
+ */
+ public Builder(
+ @NonNull String packageName,
+ @NonNull String functionId,
+ @NonNull String staticMetadataQualifiedId) {
+ super(
+ APP_FUNCTION_RUNTIME_NAMESPACE,
+ getDocumentIdForAppFunction(
+ Objects.requireNonNull(packageName),
+ Objects.requireNonNull(functionId)),
+ getRuntimeSchemaNameForPackage(packageName));
+ setPropertyString(PROPERTY_PACKAGE_NAME, packageName);
+ setPropertyString(PROPERTY_FUNCTION_ID, functionId);
+
+ // Set qualified id automatically
+ setPropertyString(
+ PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID,
+ staticMetadataQualifiedId);
+ }
+
+
+ /**
+ * Sets an indicator specifying if the function is enabled or not. This would override the
+ * default enabled state in the static metadata ({@link
+ * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}).
+ */
+ @NonNull
+ public Builder setEnabled(boolean enabled) {
+ setPropertyBoolean(PROPERTY_ENABLED, enabled);
+ return this;
+ }
+
+ /**
+ * Creates the {@link AppFunctionRuntimeMetadata} GenericDocument.
+ */
+ @NonNull
+ public AppFunctionRuntimeMetadata build() {
+ return new AppFunctionRuntimeMetadata(super.build());
+ }
+ }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
new file mode 100644
index 0000000..6d4172a
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.util.DocumentIdUtil;
+
+import java.util.Objects;
+
+/**
+ * Contains constants and helper related to static metadata represented with {@code
+ * com.android.server.appsearch.appsindexer.appsearchtypes.AppFunctionStaticMetadata}.
+ * <p>
+ * The constants listed here **must not change** and be kept consistent with the canonical
+ * static metadata class.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionStaticMetadataHelper {
+ public static final String STATIC_SCHEMA_TYPE = "AppFunctionStaticMetadata";
+ public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault";
+
+ public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions";
+
+ // These are constants that has to be kept the same with {@code
+ // com.android.server.appsearch.appsindexer.appsearchtypes.AppSearchHelper}.
+ public static final String APP_FUNCTION_STATIC_METADATA_DB = "apps-db";
+ public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
+
+ /**
+ * Returns a per-app static metadata schema name, to store all functions for that package.
+ */
+ public static String getStaticSchemaNameForPackage(@NonNull String pkg) {
+ return STATIC_SCHEMA_TYPE + "-" + Objects.requireNonNull(pkg);
+ }
+
+ /** Returns the document id for an app function's static metadata. */
+ public static String getDocumentIdForAppFunction(
+ @NonNull String pkg, @NonNull String functionId) {
+ return pkg + "/" + functionId;
+ }
+
+ /**
+ * Returns the fully qualified Id used in AppSearch for the given package and function id
+ * app function static metadata.
+ */
+ public static String getStaticMetadataQualifiedId(String packageName, String functionId) {
+ return DocumentIdUtil.createQualifiedId(
+ APP_FUNCTION_INDEXER_PACKAGE,
+ APP_FUNCTION_STATIC_METADATA_DB,
+ APP_FUNCTION_STATIC_NAMESPACE,
+ getDocumentIdForAppFunction(packageName, functionId));
+ }
+}
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index ee9114f..93d62cf 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -10,13 +10,6 @@
}
flag {
- name: "companion_transport_apis"
- namespace: "companion"
- description: "Grants access to the companion transport apis."
- bug: "288297505"
-}
-
-flag {
name: "association_tag"
is_exported: true
namespace: "companion"
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index d7a517a..ffed536 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -629,11 +629,10 @@
* A builder for {@link AttributionSource}
*/
public static final class Builder {
+ private boolean mHasBeenUsed;
private @NonNull final AttributionSourceState mAttributionSourceState =
new AttributionSourceState();
- private long mBuilderFieldsSet = 0L;
-
/**
* Creates a new Builder.
*
@@ -642,8 +641,17 @@
*/
public Builder(int uid) {
mAttributionSourceState.uid = uid;
+ mAttributionSourceState.pid = Process.INVALID_PID;
+ mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
+ mAttributionSourceState.token = sDefaultToken;
}
+ /**
+ * Creates a builder that is ready to build a new {@link AttributionSource} where
+ * all fields (primitive, immutable data, pointers) are copied from the given
+ * {@link AttributionSource}. Builder methods can still be used to mutate fields further.
+ * @param current The source to copy fields from.
+ */
public Builder(@NonNull AttributionSource current) {
if (current == null) {
throw new IllegalArgumentException("current AttributionSource can not be null");
@@ -652,10 +660,11 @@
mAttributionSourceState.pid = current.getPid();
mAttributionSourceState.packageName = current.getPackageName();
mAttributionSourceState.attributionTag = current.getAttributionTag();
- mAttributionSourceState.token = current.getToken();
mAttributionSourceState.renouncedPermissions =
current.mAttributionSourceState.renouncedPermissions;
- mBuilderFieldsSet |= 0x2 | 0x4 | 0x8 | 0x10;
+ mAttributionSourceState.deviceId = current.getDeviceId();
+ mAttributionSourceState.next = current.mAttributionSourceState.next;
+ mAttributionSourceState.token = current.getToken();
}
/**
@@ -667,7 +676,6 @@
*/
public @NonNull Builder setPid(int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x2;
mAttributionSourceState.pid = value;
return this;
}
@@ -677,7 +685,6 @@
*/
public @NonNull Builder setPackageName(@Nullable String value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x4;
mAttributionSourceState.packageName = value;
return this;
}
@@ -687,7 +694,6 @@
*/
public @NonNull Builder setAttributionTag(@Nullable String value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x8;
mAttributionSourceState.attributionTag = value;
return this;
}
@@ -718,11 +724,11 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
- public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
+ public @NonNull Builder setRenouncedPermissions(
+ @Nullable Set<String> renouncedPermissions) {
checkNotUsed();
- mBuilderFieldsSet |= 0x10;
- mAttributionSourceState.renouncedPermissions = (value != null)
- ? value.toArray(new String[0]) : null;
+ mAttributionSourceState.renouncedPermissions = (renouncedPermissions != null)
+ ? renouncedPermissions.toArray(new String[0]) : null;
return this;
}
@@ -735,7 +741,6 @@
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public @NonNull Builder setDeviceId(int deviceId) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20;
mAttributionSourceState.deviceId = deviceId;
return this;
}
@@ -745,7 +750,6 @@
*/
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x40;
mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
{value.mAttributionSourceState} : mAttributionSourceState.next;
return this;
@@ -760,7 +764,6 @@
if (value == null) {
throw new IllegalArgumentException("Null AttributionSource not permitted.");
}
- mBuilderFieldsSet |= 0x40;
mAttributionSourceState.next =
new AttributionSourceState[]{value.mAttributionSourceState};
return this;
@@ -769,28 +772,7 @@
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AttributionSource build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mAttributionSourceState.pid = Process.INVALID_PID;
- }
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mAttributionSourceState.packageName = null;
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
- mAttributionSourceState.attributionTag = null;
- }
- if ((mBuilderFieldsSet & 0x10) == 0) {
- mAttributionSourceState.renouncedPermissions = null;
- }
- if ((mBuilderFieldsSet & 0x20) == 0) {
- mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
- }
- if ((mBuilderFieldsSet & 0x40) == 0) {
- mAttributionSourceState.next = null;
- }
-
- mAttributionSourceState.token = sDefaultToken;
+ mHasBeenUsed = true;
if (mAttributionSourceState.next == null) {
// The NDK aidl backend doesn't support null parcelable arrays.
@@ -800,7 +782,7 @@
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if (mHasBeenUsed) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 92b630f..80f39bf 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -46,13 +46,13 @@
* a {@link Message} object containing a bundle of data that will be
* processed by the Handler's {@link #handleMessage} method (requiring that
* you implement a subclass of Handler).
- *
+ *
* <p>When posting or sending to a Handler, you can either
* allow the item to be processed as soon as the message queue is ready
* to do so, or specify a delay before it gets processed or absolute time for
* it to be processed. The latter two allow you to implement timeouts,
* ticks, and other timing-based behavior.
- *
+ *
* <p>When a
* process is created for your application, its main thread is dedicated to
* running a message queue that takes care of managing the top-level
@@ -85,13 +85,13 @@
*/
boolean handleMessage(@NonNull Message msg);
}
-
+
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(@NonNull Message msg) {
}
-
+
/**
* Handle system messages here.
*/
@@ -343,8 +343,8 @@
* The default implementation will either return the class name of the
* message callback if any, or the hexadecimal representation of the
* message "what" field.
- *
- * @param message The message whose name is being queried
+ *
+ * @param message The message whose name is being queried
*/
@NonNull
public String getMessageName(@NonNull Message message) {
@@ -367,7 +367,7 @@
/**
* Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
- *
+ *
* @param what Value to assign to the returned Message.what field.
* @return A Message from the global message pool.
*/
@@ -376,12 +376,12 @@
{
return Message.obtain(this, what);
}
-
+
/**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
* of the returned Message.
- *
+ *
* @param what Value to assign to the returned Message.what field.
* @param obj Value to assign to the returned Message.obj field.
* @return A Message from the global message pool.
@@ -392,7 +392,7 @@
}
/**
- *
+ *
* Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
* Message.
* @param what Value to assign to the returned Message.what field.
@@ -405,10 +405,10 @@
{
return Message.obtain(this, what, arg1, arg2);
}
-
+
/**
- *
- * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
+ *
+ * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
* returned Message.
* @param what Value to assign to the returned Message.what field.
* @param arg1 Value to assign to the returned Message.arg1 field.
@@ -423,19 +423,19 @@
/**
* Causes the Runnable r to be added to the message queue.
- * The runnable will be run on the thread to which this handler is
- * attached.
- *
+ * The runnable will be run on the thread to which this handler is
+ * attached.
+ *
* @param r The Runnable that will be executed.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
-
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
@@ -446,8 +446,8 @@
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
@@ -457,7 +457,7 @@
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
-
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <var>uptimeMillis</var>.
@@ -470,21 +470,21 @@
* {@link #removeCallbacksAndMessages}.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
- *
+ *
* @see android.os.SystemClock#uptimeMillis
*/
public final boolean postAtTime(
@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
-
+
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
@@ -492,12 +492,12 @@
* is attached.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
- *
+ *
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* will be executed.
- *
- * @return Returns true if the Runnable was successfully placed in to the
+ *
+ * @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the Runnable will be processed --
@@ -507,7 +507,7 @@
public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
-
+
/** @hide */
public final boolean postDelayed(Runnable r, int what, long delayMillis) {
return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
@@ -547,10 +547,10 @@
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
- *
+ *
* @param r The Runnable that will be executed.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -635,8 +635,8 @@
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -646,8 +646,8 @@
/**
* Sends a Message containing only the what value.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -659,9 +659,9 @@
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
- * @see #sendMessageDelayed(android.os.Message, long)
- *
- * @return Returns true if the message was successfully placed in to the
+ * @see #sendMessageDelayed(android.os.Message, long)
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -672,11 +672,11 @@
}
/**
- * Sends a Message containing only the what value, to be delivered
+ * Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -691,8 +691,8 @@
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
@@ -713,12 +713,12 @@
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
- *
+ *
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
@@ -743,8 +743,8 @@
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
- *
- * @return Returns true if the message was successfully placed in to the
+ *
+ * @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
@@ -798,6 +798,12 @@
/**
* Remove any pending posts of messages with code 'what' that are in the
* message queue.
+ *
+ * Note that `Message#what` is 0 unless otherwise set.
+ * When calling `postMessage(Runnable)` or `postAtTime(Runnable, long)`,
+ * the `Runnable` is internally wrapped with a `Message` whose `what` is 0.
+ * Calling `removeMessages(0)` will remove all messages without a `what`,
+ * including posted `Runnable`s.
*/
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
@@ -889,7 +895,7 @@
}
// if we can get rid of this method, the handler need not remember its loop
- // we could instead export a getMessageQueue() method...
+ // we could instead export a getMessageQueue() method...
@NonNull
public final Looper getLooper() {
return mLooper;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index a1db9be..702fdc2 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -41,6 +41,9 @@
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
+ *
+ * If not specified, this value is 0.
+ * Use values other than 0 to indicate custom message codes.
*/
public int what;
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index c801fabf..a15b3bb 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -379,6 +379,12 @@
private int mReferenceCount;
private MemoryRegistration(int size) {
+ // Round up to the nearest page size
+ final int PAGE_SIZE = OsConstants._SC_PAGE_SIZE;
+ final int remainder = size % PAGE_SIZE;
+ if (remainder != 0) {
+ size += PAGE_SIZE - remainder;
+ }
mSize = size;
mReferenceCount = 1;
VMRuntime.getRuntime().registerNativeAllocation(mSize);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 536eca6..f1ec0e4e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1978,7 +1978,6 @@
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- @FlaggedApi(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED)
public static final String DISALLOW_SIM_GLOBALLY =
"no_sim_globally";
@@ -1999,7 +1998,6 @@
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- @FlaggedApi(android.app.admin.flags.Flags.FLAG_ASSIST_CONTENT_USER_RESTRICTION_ENABLED)
public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
/**
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index fe536a6..22583ac 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -19,6 +19,7 @@
import android.annotation.HalfFloat;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import libcore.util.FP16;
@@ -92,6 +93,7 @@
* <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
*/
@SuppressWarnings("SimplifiableIfStatement")
+@RavenwoodKeepWholeClass
public final class Half extends Number implements Comparable<Half> {
/**
* The number of bits used to represent a half-precision float value.
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index 8c6721a..efacc34 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -49,3 +49,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "avoid_rebinding_intentionally_disconnected_wallpaper"
+ namespace: "systemui"
+ description: "Prevents rebinding with intentionally disconnected wallpaper services."
+ bug: "332871851"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e14249c..e0529b3 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3333,6 +3333,7 @@
Bundle args = new Bundle();
args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId());
args.putLong(Intent.EXTRA_TIME, event.getEventTime());
+ args.putBoolean(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, true);
((SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE))
.launchAssist(args);
return true;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a89ab45..17ff2eb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3766,7 +3766,6 @@
privileged app such as the Assistant app.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"
android:protectionLevel="internal|role" />
@@ -4027,7 +4026,6 @@
APIs protected by this permission on users different to the calling user.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.esim_management_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"
android:protectionLevel="internal|role" />
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 94bde68..127dbfd 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BATTERY_STATS"/>
+ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
@@ -31,7 +32,8 @@
<activity android:name=".BatteryConsumerPickerActivity"
android:label="Battery Stats"
android:launchMode="singleTop"
- android:exported="true">
+ android:exported="true"
+ android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -41,5 +43,25 @@
<activity android:name=".BatteryStatsViewerActivity"
android:label="Battery Stats"
android:parentActivityName=".BatteryConsumerPickerActivity"/>
+
+ <activity android:name=".TrampolineActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.NoDisplay">
+ <intent-filter>
+ <action android:name="com.android.settings.action.IA_SETTINGS"/>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ <meta-data android:name="com.android.settings.category"
+ android:value="com.android.settings.category.ia.development" />
+ <meta-data android:name="com.android.settings.title"
+ android:resource="@string/settings_title" />
+ <meta-data android:name="com.android.settings.summary"
+ android:resource="@string/settings_summary" />
+ <meta-data android:name="com.android.settings.group_key"
+ android:value="debug_debugging_category" />
+ <meta-data android:name="com.android.settings.order"
+ android:value="2" />
+ </activity>
</application>
</manifest>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml
new file mode 100644
index 0000000..c23c148
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_title">Launch Battery Stats Viewer</string>
+ <string name="settings_summary">The Battery Stats Viewer will be visible in the Launcher after it is opened once.</string>
+</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
new file mode 100644
index 0000000..b016488
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.frameworks.core.batterystatsviewer;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+public class TrampolineActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ showLauncherIcon();
+ launchMainActivity();
+ }
+
+ private void showLauncherIcon() {
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(new ComponentName(this, BatteryConsumerPickerActivity.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+
+ private void launchMainActivity() {
+ startActivity(new Intent(this, BatteryConsumerPickerActivity.class));
+ }
+}
diff --git a/data/keyboards/Vendor_0957_Product_0033.idc b/data/keyboards/Vendor_0957_Product_0033.idc
new file mode 100644
index 0000000..7dfbe2c
--- /dev/null
+++ b/data/keyboards/Vendor_0957_Product_0033.idc
@@ -0,0 +1,23 @@
+# Copyright 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.
+#
+
+# Input Device Configuration file for Google Reference RCU Remote.
+# PID 0033 is for new G20 with start button.
+
+# Basic Parameters
+keyboard.layout = Vendor_0957_Product_0031
+# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java
index a99e201..683f614 100644
--- a/graphics/java/android/graphics/Matrix44.java
+++ b/graphics/java/android/graphics/Matrix44.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import com.android.graphics.hwui.flags.Flags;
@@ -30,6 +31,7 @@
* in row-major order. The values and operations are treated as column vectors.
*/
@FlaggedApi(Flags.FLAG_MATRIX_44)
+@RavenwoodKeepWholeClass
public class Matrix44 {
final float[] mBackingArray;
/**
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 618e6dc..c7b8941 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.drawable.Drawable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -35,6 +36,7 @@
* @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
* @see Drawable#getOutline(Outline)
*/
+@RavenwoodKeepWholeClass
public final class Outline {
private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index 748d66c..76c17154 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
/**
* A {@link Parcelable} wrapper for a {@link ColorSpace}. In order to enable parceling, the
@@ -27,6 +28,7 @@
* {@link ColorSpace.Rgb} instance that has an ICC parametric transfer function as returned by
* {@link ColorSpace.Rgb#getTransferParameters()}.
*/
+@RavenwoodKeepWholeClass
public final class ParcelableColorSpace implements Parcelable {
private final ColorSpace mColorSpace;
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 3ec5b9c..a872e03 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -17,10 +17,12 @@
package android.graphics;
import android.annotation.IntDef;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+@RavenwoodKeepWholeClass
public class PixelFormat {
/** @hide */
@IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 90b9798..cbd4a52 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -181,6 +181,13 @@
super.taskBarWindowIsAlwaysVisible()
}
+ // Overridden to remove @Postsubmit annotation
+ @Test
+ @FlakyTest(bugId = 294993100)
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ // No rounded corners as we go back to fullscreen in new orientation.
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index ed2a0a7..578a9b5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -147,6 +147,12 @@
@Test
override fun entireScreenCovered() = super.entireScreenCovered()
+ @Postsubmit
+ @Test
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) }
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
index 8cb81b4..f57335c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
@@ -70,6 +70,11 @@
}
}
+ @Test
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ // PiP might have completely faded out by this point, so corner radii not applicable.
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
index 0742cf9..ce84eb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip.common
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -123,6 +124,12 @@
}
}
+ @Postsubmit
+ @Test
+ override fun pipLayerHasCorrectCornersAtEnd() {
+ flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) }
+ }
+
/** {@inheritDoc} */
@Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index 99c1ad2..bc2bfdb 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -18,6 +18,7 @@
import android.app.Instrumentation
import android.content.Intent
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.Rotation
import android.tools.flicker.legacy.FlickerBuilder
@@ -105,4 +106,10 @@
.doesNotContain(false)
}
}
+
+ @Postsubmit
+ @Test
+ open fun pipLayerHasCorrectCornersAtEnd() {
+ flicker.assertLayersEnd { hasRoundedCorners(pipApp) }
+ }
}
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 8a25726..0d4ef3c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="28"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="0dp"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
index 7e626e5..3072772 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="28"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
<corners
android:topLeftRadius="0dp"
android:topRightRadius="?android:attr/dialogCornerRadius"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
index 9f4980b..f1790f9 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
@@ -20,7 +20,7 @@
xmlns:tools="http://schemas.android.com/tools"
tools:targetApi="28"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
<corners
android:radius="?android:attr/dialogCornerRadius"
/>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
index 67b5107..f0da7b4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
@@ -18,7 +18,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface" />
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
<corners
android:radius="0dp"
/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 055afed..0ffb763 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,5 +1,6 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
import android.annotation.SuppressLint;
@@ -704,12 +705,50 @@
return !sourceList.isEmpty() && sourceList.stream().anyMatch(BluetoothUtils::isConnected);
}
+ /**
+ * Check if {@link BluetoothDevice} has a active local broadcast source.
+ *
+ * @param device The bluetooth device to check.
+ * @param localBtManager The BT manager to provide BT functions.
+ * @return Whether the device has a active local broadcast source.
+ */
+ @WorkerThread
+ public static boolean hasActiveLocalBroadcastSourceForBtDevice(
+ @Nullable BluetoothDevice device, @Nullable LocalBluetoothManager localBtManager) {
+ LocalBluetoothLeBroadcastAssistant assistant =
+ localBtManager == null
+ ? null
+ : localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+ LocalBluetoothLeBroadcast broadcast =
+ localBtManager == null
+ ? null
+ : localBtManager.getProfileManager().getLeAudioBroadcastProfile();
+ if (device == null || assistant == null || broadcast == null) {
+ Log.d(TAG, "Skip check hasActiveLocalBroadcastSourceForBtDevice due to arg is null");
+ return false;
+ }
+ List<BluetoothLeBroadcastReceiveState> sourceList = assistant.getAllSources(device);
+ int broadcastId = broadcast.getLatestBroadcastId();
+ return !sourceList.isEmpty()
+ && broadcastId != UNKNOWN_VALUE_PLACEHOLDER
+ && sourceList.stream()
+ .anyMatch(
+ source -> isSourceMatched(source, broadcastId));
+ }
+
/** Checks the connectivity status based on the provided broadcast receive state. */
@WorkerThread
public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
return state.getBisSyncState().stream().anyMatch(bitmap -> bitmap != 0);
}
+ /** Checks if the broadcast id is matched based on the provided broadcast receive state. */
+ @WorkerThread
+ public static boolean isSourceMatched(
+ @Nullable BluetoothLeBroadcastReceiveState state, int broadcastId) {
+ return state != null && state.getBroadcastId() == broadcastId;
+ }
+
/**
* Checks if the Bluetooth device is an available hearing device, which means: 1) currently
* connected 2) is Hearing Aid 3) connected profile match hearing aid related profiles (e.g.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 26905b1..6f2567b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -101,7 +101,7 @@
private static final int DEFAULT_CODE_MIN = 1000;
// Order of this profile in device profiles list
private static final int ORDINAL = 1;
- private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+ static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
private static final Uri[] SETTINGS_URIS =
new Uri[] {
Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_NAME),
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 3a7b0c7..a0e764a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
import static com.android.settingslib.bluetooth.BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA;
import static com.google.common.truth.Truth.assertThat;
@@ -96,6 +97,7 @@
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
+ private static final int TEST_BROADCAST_ID = 25;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -671,6 +673,34 @@
}
@Test
+ public void testHasActiveLocalBroadcastSourceForBtDevice_hasActiveLocalSource() {
+ when(mBroadcast.getLatestBroadcastId()).thenReturn(TEST_BROADCAST_ID);
+ when(mLeBroadcastReceiveState.getBroadcastId()).thenReturn(TEST_BROADCAST_ID);
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ sourceList.add(mLeBroadcastReceiveState);
+ when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+ assertThat(
+ BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+ mBluetoothDevice, mLocalBluetoothManager))
+ .isTrue();
+ }
+
+ @Test
+ public void testHasActiveLocalBroadcastSourceForBtDevice_noActiveLocalSource() {
+ when(mLeBroadcastReceiveState.getBroadcastId()).thenReturn(UNKNOWN_VALUE_PLACEHOLDER);
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ sourceList.add(mLeBroadcastReceiveState);
+ when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+ assertThat(
+ BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+ mBluetoothDevice, mLocalBluetoothManager))
+ .isFalse();
+ }
+
+
+ @Test
public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() {
when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c1bb55c..d26a906 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -949,36 +949,37 @@
strict_mode: false,
}
-// Disable for now. TODO(b/356666754) Re-enable it
-// android_ravenwood_test {
-// name: "SystemUiRavenTests",
-// srcs: [
-// ":SystemUI-tests-utils",
-// ":SystemUI-tests-multivalent",
-// // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable
-// // use_resource_processor: true when better supported by soong
-// ":SystemUIRobo-stub{.aapt.srcjar}",
-// ],
-// static_libs: [
-// "SystemUI-core",
-// "SystemUI-res",
-// "SystemUI-tests-base",
-// "androidx.test.uiautomator_uiautomator",
-// "androidx.core_core-animation-testing",
-// "androidx.test.ext.junit",
-// "kosmos",
-// "mockito-kotlin-nodeps",
-// ],
-// libs: [
-// "android.test.runner",
-// "android.test.base",
-// "android.test.mock",
-// ],
-// auto_gen_config: true,
-// plugins: [
-// "dagger2-compiler",
-// ],
-// }
+android_ravenwood_test {
+ name: "SystemUiRavenTests",
+ srcs: [
+ ":SystemUI-tests-utils",
+ ":SystemUI-tests-multivalent",
+ // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable
+ // use_resource_processor: true when better supported by soong
+ ":SystemUIRobo-stub{.aapt.srcjar}",
+ ],
+ static_libs: [
+ "SystemUI-core",
+ "SystemUI-res",
+ "SystemUI-tests-base",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.core_core-animation-testing",
+ "androidx.test.ext.junit",
+ "kosmos",
+ "kotlin-test",
+ "mockito-kotlin-nodeps",
+ "androidx.compose.runtime_runtime",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ auto_gen_config: true,
+ plugins: [
+ "dagger2-compiler",
+ ],
+}
// Opt-out config for optimizing the SystemUI target using R8.
// Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 8a1d81b..97206de 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -139,13 +139,6 @@
}
flag {
- name: "notifications_heads_up_refactor"
- namespace: "systemui"
- description: "Use HeadsUpInteractor to feed HUN updates to the NSSL."
- bug: "325936094"
-}
-
-flag {
name: "notification_transparent_header_fix"
namespace: "systemui"
description: "fix the transparent group header issue for async header inflation."
@@ -370,6 +363,13 @@
}
flag {
+ name: "status_bar_signal_policy_refactor"
+ namespace: "systemui"
+ description: "Use a settings observer for airplane mode and make StatusBarSignalPolicy startable"
+ bug: "264539100"
+}
+
+flag {
name: "status_bar_swipe_over_chip"
namespace: "systemui"
description: "Allow users to swipe over the status bar chip to open the shade"
@@ -380,6 +380,17 @@
}
flag {
+ name: "status_bar_always_check_underlying_networks"
+ namespace: "systemui"
+ description: "For status bar connectivity UI, always check underlying networks for wifi and "
+ "carrier merged information, regardless of the sepcified transport type"
+ bug: "352162710"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "status_bar_stop_updating_window_height"
namespace: "systemui"
description: "Don't have PhoneStatusBarView manually trigger an update of the height in "
@@ -391,6 +402,13 @@
}
flag {
+ name: "status_bar_ron_chips"
+ namespace: "systemui"
+ description: "Show rich ongoing notifications as chips in the status bar"
+ bug: "361346412"
+}
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 7707a60..fe9105e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -29,6 +29,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
+import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -56,7 +57,6 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import com.android.systemui.Flags as AconfigFlags
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -66,8 +66,7 @@
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
@Mock private lateinit var passwordEntry: EditText
- private var passwordEntryLayoutParams =
- ViewGroup.LayoutParams(/* width = */ 0, /* height = */ 0)
+ private var passwordEntryLayoutParams = ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0)
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode
@Mock lateinit var lockPatternUtils: LockPatternUtils
@@ -106,6 +105,8 @@
whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button))
.thenReturn(mock(ImageView::class.java))
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
+ // TODO(b/362362385): No need to mock keyguardPasswordView.context once this bug is fixed.
+ `when`(keyguardPasswordView.context).thenReturn(context)
whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams)
val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
val fakeFeatureFlags = FakeFeatureFlags()
@@ -187,9 +188,11 @@
verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture())
val eventHandled =
- keyListenerArgumentCaptor.value.onKey(keyguardPasswordView,
+ keyListenerArgumentCaptor.value.onKey(
+ keyguardPasswordView,
KeyEvent.KEYCODE_SPACE,
- KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE))
+ KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+ )
assertFalse("Unlock attempted.", eventHandled)
}
@@ -204,9 +207,11 @@
verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture())
val eventHandled =
- keyListenerArgumentCaptor.value.onKey(keyguardPasswordView,
+ keyListenerArgumentCaptor.value.onKey(
+ keyguardPasswordView,
KeyEvent.KEYCODE_ENTER,
- KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
+ KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)
+ )
assertTrue("Unlock not attempted.", eventHandled)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index f7f69d3..ca585ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -56,6 +56,7 @@
import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardDismissTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.Kosmos
@@ -279,7 +280,7 @@
deviceProvisionedController,
faceAuthAccessibilityDelegate,
devicePolicyManager,
- keyguardTransitionInteractor,
+ kosmos.keyguardDismissTransitionInteractor,
{ primaryBouncerInteractor },
) {
deviceEntryInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
index d244482..58c3fec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -35,15 +37,14 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.scrim.ScrimController;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeUserTracker;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -57,7 +58,6 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Collections;
import java.util.Optional;
@SmallTest
@@ -106,15 +106,13 @@
UiEventLogger mUiEventLogger;
@Mock
- LockPatternUtils mLockPatternUtils;
-
- @Mock
ActivityStarter mActivityStarter;
@Mock
CommunalViewModel mCommunalViewModel;
- FakeUserTracker mUserTracker;
+ @Mock
+ KeyguardInteractor mKeyguardInteractor;
private static final float TOUCH_REGION = .3f;
private static final float MIN_BOUNCER_HEIGHT = .05f;
@@ -130,7 +128,6 @@
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
MockitoAnnotations.initMocks(this);
- mUserTracker = new FakeUserTracker();
mTouchHandler = new BouncerSwipeTouchHandler(
mKosmos.getTestScope(),
mScrimManager,
@@ -138,24 +135,21 @@
mNotificationShadeWindowController,
mValueAnimatorCreator,
mVelocityTrackerFactory,
- mLockPatternUtils,
- mUserTracker,
mCommunalViewModel,
mFlingAnimationUtils,
mFlingAnimationUtilsClosing,
TOUCH_REGION,
MIN_BOUNCER_HEIGHT,
mUiEventLogger,
- mActivityStarter);
+ mActivityStarter,
+ mKeyguardInteractor);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
- when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true);
-
- mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0);
+ when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
}
/**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index b85e32b..9568167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
@@ -44,16 +46,15 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.scrim.ScrimController;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeUserTracker;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -69,7 +70,6 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Collections;
import java.util.Optional;
@SmallTest
@@ -116,9 +116,6 @@
UiEventLogger mUiEventLogger;
@Mock
- LockPatternUtils mLockPatternUtils;
-
- @Mock
ActivityStarter mActivityStarter;
@Mock
@@ -127,11 +124,12 @@
@Mock
CommunalViewModel mCommunalViewModel;
+ @Mock
+ KeyguardInteractor mKeyguardInteractor;
+
@Captor
ArgumentCaptor<Rect> mRectCaptor;
- FakeUserTracker mUserTracker;
-
private static final float TOUCH_REGION = .3f;
private static final int SCREEN_WIDTH_PX = 1024;
private static final int SCREEN_HEIGHT_PX = 100;
@@ -148,7 +146,6 @@
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
MockitoAnnotations.initMocks(this);
- mUserTracker = new FakeUserTracker();
mTouchHandler = new BouncerSwipeTouchHandler(
mKosmos.getTestScope(),
mScrimManager,
@@ -156,24 +153,21 @@
mNotificationShadeWindowController,
mValueAnimatorCreator,
mVelocityTrackerFactory,
- mLockPatternUtils,
- mUserTracker,
mCommunalViewModel,
mFlingAnimationUtils,
mFlingAnimationUtilsClosing,
TOUCH_REGION,
MIN_BOUNCER_HEIGHT,
mUiEventLogger,
- mActivityStarter);
+ mActivityStarter,
+ mKeyguardInteractor);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
- when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true);
-
- mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0);
+ when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
}
/**
@@ -391,7 +385,7 @@
*/
@Test
public void testSwipeUp_keyguardNotSecure_doesNotExpand() {
- when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false);
+ when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(true));
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
@@ -426,7 +420,7 @@
*/
@Test
public void testSwipeDown_keyguardNotSecure_doesNotExpand() {
- when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false);
+ when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(true));
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index a068118..d21a827 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -92,8 +92,11 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
+import org.mockito.kotlin.firstValue
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verifyZeroInteractions
import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@@ -423,6 +426,47 @@
}
@Test
+ fun testDeferredResetRespondsToAnimationEnd() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*isPreview*/,
+ true /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+
+ whenever(mStateController.areExitAnimationsRunning()).thenReturn(true)
+ clearInvocations(mStateController, mTouchMonitor)
+
+ // Starting a dream will cause it to end first.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*isPreview*/,
+ true /*shouldShowComplication*/
+ )
+
+ mMainExecutor.runAllReady()
+
+ verifyZeroInteractions(mTouchMonitor)
+
+ val captor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback::class.java)
+ verify(mStateController).addCallback(captor.capture())
+
+ whenever(mStateController.areExitAnimationsRunning()).thenReturn(false)
+
+ captor.firstValue.onStateChanged()
+
+ // Should only be called once since it should be null during the second reset.
+ verify(mTouchMonitor).destroy()
+ }
+
+ @Test
fun testLowLightSetByStartDream() {
val client = client
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 3b8ffcd..17e3006 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -116,7 +116,7 @@
reset(transitionRepository)
// Authentication results in calling startDismissKeyguardTransition.
- kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
+ kosmos.keyguardDismissTransitionInteractor.startDismissKeyguardTransition()
runCurrent()
assertThat(transitionRepository)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 6eb9862..33f3cd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -39,12 +39,15 @@
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
@@ -53,6 +56,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.testKosmos
import junit.framework.Assert.assertEquals
@@ -166,6 +170,10 @@
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
testScope.runTest {
+ val isGone by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
@@ -175,7 +183,7 @@
runCurrent()
// Make sure we're GONE.
- assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+ assertEquals(true, isGone)
// Get part way to AOD.
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -204,6 +212,10 @@
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
testScope.runTest {
+ val isGone by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
@@ -213,7 +225,7 @@
runCurrent()
// Make sure we're GONE.
- assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+ assertEquals(true, isGone)
// Get all the way to AOD
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -239,6 +251,10 @@
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
testScope.runTest {
+ val isLockscreen by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Lockscreen, LOCKSCREEN)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
@@ -248,10 +264,7 @@
runCurrent()
// Make sure we're in LOCKSCREEN.
- assertEquals(
- KeyguardState.LOCKSCREEN,
- kosmos.keyguardTransitionInteractor.getFinishedState()
- )
+ assertEquals(true, isLockscreen)
// Get part way to AOD.
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -314,7 +327,7 @@
testScope.runTest {
kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
- kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
+ kosmos.keyguardDismissTransitionInteractor.startDismissKeyguardTransition()
powerInteractor.setAwakeForTest()
advanceTimeBy(100) // account for debouncing
@@ -327,6 +340,10 @@
@DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetectedInAod_fromGone() =
testScope.runTest {
+ val isGone by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.AOD,
@@ -336,7 +353,7 @@
runCurrent()
// Make sure we're GONE.
- assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+ assertEquals(true, isGone)
// Start going to AOD on first button push
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index ee4a0d2d..c18deb1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -48,12 +48,15 @@
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -62,6 +65,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -316,6 +320,10 @@
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
testScope.runTest {
+ val isGone by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.DOZING,
@@ -325,7 +333,7 @@
runCurrent()
// Make sure we're GONE.
- assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+ assertEquals(true, isGone)
// Get part way to AOD.
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -355,6 +363,10 @@
@Suppress("ktlint:standard:max-line-length")
fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
testScope.runTest {
+ val isGone by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.DOZING,
@@ -364,7 +376,7 @@
runCurrent()
// Make sure we're GONE.
- assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+ assertEquals(true, isGone)
// Get all the way to AOD
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -390,6 +402,10 @@
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
testScope.runTest {
+ val isLockscreen by
+ collectLastValue(
+ kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Lockscreen, LOCKSCREEN)
+ )
powerInteractor.setAwakeForTest()
transitionRepository.sendTransitionSteps(
from = KeyguardState.DOZING,
@@ -399,10 +415,7 @@
runCurrent()
// Make sure we're in LOCKSCREEN.
- assertEquals(
- KeyguardState.LOCKSCREEN,
- kosmos.keyguardTransitionInteractor.getFinishedState()
- )
+ assertEquals(true, isLockscreen)
// Get part way to AOD.
powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 6e76cbc..6708ffa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -90,52 +90,6 @@
}
@Test
- fun finishedKeyguardStateTests() =
- testScope.runTest {
- val finishedSteps by collectValues(underTest.finishedKeyguardState)
- runCurrent()
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
- steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
- steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
- steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
- }
-
- assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD))
- }
-
- @Test
- fun startedKeyguardStateTests() =
- testScope.runTest {
- val startedStates by collectValues(underTest.startedKeyguardState)
- runCurrent()
- val steps = mutableListOf<TransitionStep>()
-
- steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
- steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
- steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
- steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
- steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
- }
-
- assertThat(startedStates).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD, GONE))
- }
-
- @Test
fun startedKeyguardTransitionStepTests() =
testScope.runTest {
val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)
@@ -1206,95 +1160,6 @@
}
@Test
- fun finishedKeyguardState_emitsAgainIfCancelledAndReversed() =
- testScope.runTest {
- val finishedStates by collectValues(underTest.finishedKeyguardState)
-
- // We default FINISHED in LOCKSCREEN.
- assertEquals(listOf(LOCKSCREEN), finishedStates)
-
- sendSteps(
- TransitionStep(LOCKSCREEN, AOD, 0f, STARTED),
- TransitionStep(LOCKSCREEN, AOD, 0.5f, RUNNING),
- TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED),
- )
-
- // We're FINISHED in AOD.
- assertEquals(
- listOf(
- LOCKSCREEN,
- AOD,
- ),
- finishedStates
- )
-
- // Transition back to LOCKSCREEN.
- sendSteps(
- TransitionStep(AOD, LOCKSCREEN, 0f, STARTED),
- TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING),
- TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED),
- )
-
- // We're FINISHED in LOCKSCREEN.
- assertEquals(
- listOf(
- LOCKSCREEN,
- AOD,
- LOCKSCREEN,
- ),
- finishedStates
- )
-
- sendSteps(
- TransitionStep(LOCKSCREEN, GONE, 0f, STARTED),
- TransitionStep(LOCKSCREEN, GONE, 0.5f, RUNNING),
- )
-
- // We've STARTED a transition to GONE but not yet finished it so we're still FINISHED in
- // LOCKSCREEN.
- assertEquals(
- listOf(
- LOCKSCREEN,
- AOD,
- LOCKSCREEN,
- ),
- finishedStates
- )
-
- sendSteps(
- TransitionStep(LOCKSCREEN, GONE, 0.6f, CANCELED),
- )
-
- // We've CANCELED a transition to GONE, we're still FINISHED in LOCKSCREEN.
- assertEquals(
- listOf(
- LOCKSCREEN,
- AOD,
- LOCKSCREEN,
- ),
- finishedStates
- )
-
- sendSteps(
- TransitionStep(GONE, LOCKSCREEN, 0.6f, STARTED),
- TransitionStep(GONE, LOCKSCREEN, 0.9f, RUNNING),
- TransitionStep(GONE, LOCKSCREEN, 1f, FINISHED),
- )
-
- // Expect another emission of LOCKSCREEN, as we have FINISHED a second transition to
- // LOCKSCREEN after the cancellation.
- assertEquals(
- listOf(
- LOCKSCREEN,
- AOD,
- LOCKSCREEN,
- LOCKSCREEN,
- ),
- finishedStates
- )
- }
-
- @Test
fun testCurrentState() =
testScope.runTest {
val currentStates by collectValues(underTest.currentKeyguardState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 3e0a1f3..073ed61 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -19,18 +19,20 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
+import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_EFFECT
import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.testKosmos
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -64,35 +66,45 @@
@Test
fun lightRevealEffect_doesNotChangeDuringKeyguardTransition() =
- runTest(UnconfinedTestDispatcher()) {
- val values = mutableListOf<LightRevealEffect>()
- val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
+ kosmos.testScope.runTest {
+ val values by collectValues(underTest.lightRevealEffect)
+ runCurrent()
+ assertEquals(listOf(DEFAULT_REVEAL_EFFECT), values)
fakeLightRevealScrimRepository.setRevealEffect(reveal1)
-
+ runCurrent()
// The reveal effect shouldn't emit anything until a keyguard transition starts.
- assertEquals(values.size, 0)
+ assertEquals(listOf(DEFAULT_REVEAL_EFFECT), values)
// Once it starts, it should emit reveal1.
fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(transitionState = TransitionState.STARTED)
+ TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED)
)
- assertEquals(values, listOf(reveal1))
+ runCurrent()
+ assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1), values)
// Until the next transition starts, reveal2 should not be emitted.
fakeLightRevealScrimRepository.setRevealEffect(reveal2)
+ runCurrent()
fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(transitionState = TransitionState.RUNNING)
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.RUNNING
+ )
)
+ runCurrent()
fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(transitionState = TransitionState.FINISHED)
+ TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.FINISHED)
)
- assertEquals(values, listOf(reveal1))
+ runCurrent()
+ assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1), values)
fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(transitionState = TransitionState.STARTED)
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED
+ )
)
- assertEquals(values, listOf(reveal1, reveal2))
-
- job.cancel()
+ runCurrent()
+ assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1, reveal2), values)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
index 45a8b3e..db58c85 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
@@ -46,7 +46,6 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,14 +63,10 @@
private val underTest by lazy { kosmos.quickSettingsShadeSceneActionsViewModel }
- @Before
- fun setUp() {
- underTest.activateIn(testScope)
- }
-
@Test
fun upTransitionSceneKey_deviceLocked_lockscreen() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
lockDevice()
@@ -85,6 +80,7 @@
@Test
fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
lockDevice()
@@ -98,6 +94,7 @@
@Test
fun upTransitionSceneKey_deviceUnlocked_gone() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
lockDevice()
@@ -113,6 +110,7 @@
fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
testScope.runTest {
kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
lockDevice()
@@ -127,6 +125,7 @@
fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
testScope.runTest {
kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
lockDevice()
@@ -141,6 +140,7 @@
@Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
@@ -157,6 +157,7 @@
@Test
fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
@@ -174,6 +175,7 @@
@Test
fun backTransitionSceneKey_notEditing_Home() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
assertThat((actions?.get(Back) as? UserActionResult.ChangeScene)?.toScene)
@@ -183,6 +185,7 @@
@Test
fun backTransition_editing_noDestination() =
testScope.runTest {
+ underTest.activateIn(this)
val actions by collectLastValue(underTest.actions)
kosmos.editModeViewModel.startEditing()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
index dd4432d..900f2a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
@@ -35,7 +35,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -123,7 +122,7 @@
override suspend fun hydrateActions(
setActions: (Map<UserAction, UserActionResult>) -> Unit,
) {
- upstream.collectLatest { setActions(it) }
+ upstream.collect { setActions(it) }
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index 14134cc..cea8857 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -18,11 +18,11 @@
package com.android.systemui.statusbar.notification.domain.interactor
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -31,7 +31,6 @@
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.testKosmos
@@ -44,7 +43,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+@EnableSceneContainer
class HeadsUpNotificationInteractorTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index f96cf10..840aa92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -41,7 +42,6 @@
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
@@ -535,7 +535,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun pinnedHeadsUpRows_filtersForPinnedItems() =
testScope.runTest {
val pinnedHeadsUpRows by collectLastValue(underTest.pinnedHeadsUpRows)
@@ -576,7 +576,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun hasPinnedHeadsUpRows_true() =
testScope.runTest {
val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow)
@@ -591,7 +591,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun hasPinnedHeadsUpRows_false() =
testScope.runTest {
val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow)
@@ -606,7 +606,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun topHeadsUpRow_emptyList_null() =
testScope.runTest {
val topHeadsUpRow by collectLastValue(underTest.topHeadsUpRow)
@@ -618,7 +618,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun headsUpAnimationsEnabled_true() =
testScope.runTest {
val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
@@ -631,7 +631,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun headsUpAnimationsEnabled_keyguardShowing_true() =
testScope.runTest {
val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
index ca106fa..469a7bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
@@ -23,6 +23,7 @@
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -36,7 +37,6 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -469,9 +469,9 @@
@get:Parameters(name = "{0}")
val flags: List<FlagsParameterization>
get() = buildList {
- addAll(FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME))
addAll(
- FlagsParameterization.allCombinationsOf(NotificationsHeadsUpRefactor.FLAG_NAME)
+ FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
+ .andSceneContainer()
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index 51a70bd..fe6c741 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.fakeConfigurationController
import com.android.systemui.testKosmos
@@ -157,6 +158,10 @@
@Test
fun testDumpingState() =
test({
+ testableResources.addOverride(R.bool.volume_panel_is_large_screen, false)
+ testableResources.overrideConfiguration(
+ Configuration().apply { orientation = Configuration.ORIENTATION_PORTRAIT }
+ )
componentByKey =
mapOf(
COMPONENT_1 to mockVolumePanelUiComponentProvider,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 64fe78d..7ec977a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -218,6 +218,7 @@
@ViewDebug.ExportedProperty(category="recents")
public String title;
@ViewDebug.ExportedProperty(category="recents")
+ @Nullable
public String titleDescription;
@ViewDebug.ExportedProperty(category="recents")
public int colorPrimary;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 490ad5c..3ad73bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -19,9 +19,12 @@
import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
@@ -170,8 +173,33 @@
mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
mPasswordEntry.setOnKeyListener(mKeyListener);
mPasswordEntry.addTextChangedListener(mTextWatcher);
+
// Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
+ // TODO(b/362362385): Revert to the previous onClickListener implementation once this bug is
+ // fixed.
+ mPasswordEntry.setOnClickListener(new View.OnClickListener() {
+
+ private final boolean mAutomotiveAndVisibleBackgroundUsers =
+ isAutomotiveAndVisibleBackgroundUsers();
+
+ @Override
+ public void onClick(View v) {
+ if (mAutomotiveAndVisibleBackgroundUsers) {
+ mInputMethodManager.restartInput(v);
+ }
+ mKeyguardSecurityCallback.userActivity();
+ }
+
+ private boolean isAutomotiveAndVisibleBackgroundUsers() {
+ final Context context = getContext();
+ return context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)
+ && UserManager.isVisibleBackgroundUsersEnabled()
+ && context.getResources().getBoolean(
+ android.R.bool.config_perDisplayFocusEnabled);
+ }
+ });
+
mSwitchImeButton.setOnClickListener(v -> {
mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
// Do not show auxiliary subtypes in password lock screen.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index afd42cb..61f9800 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -81,7 +81,7 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -134,7 +134,7 @@
private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
private final BouncerMessageInteractor mBouncerMessageInteractor;
private int mTranslationY;
- private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor;
private final DevicePolicyManager mDevicePolicyManager;
// Whether the volume keys should be handled by keyguard. If true, then
// they will be handled here for specific media types such as music, otherwise
@@ -321,7 +321,7 @@
}
if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+ mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition(
"KeyguardSecurityContainerController#finish");
}
}
@@ -458,7 +458,7 @@
DeviceProvisionedController deviceProvisionedController,
FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
DevicePolicyManager devicePolicyManager,
- KeyguardTransitionInteractor keyguardTransitionInteractor,
+ KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor,
Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor,
Provider<DeviceEntryInteractor> deviceEntryInteractor
) {
@@ -490,7 +490,7 @@
mSelectedUserInteractor = selectedUserInteractor;
mDeviceEntryInteractor = deviceEntryInteractor;
mJavaAdapter = javaAdapter;
- mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mKeyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor;
mDeviceProvisionedController = deviceProvisionedController;
mPrimaryBouncerInteractor = primaryBouncerInteractor;
mDevicePolicyManager = devicePolicyManager;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9b45fa4..f731186 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -3811,7 +3811,8 @@
if (!mSimDatas.containsKey(subId)) {
refreshSimState(subId, SubscriptionManager.getSlotIndex(subId));
}
- return mSimDatas.get(subId).slotId;
+ SimData simData = mSimDatas.get(subId);
+ return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
index a093f58..fd06bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -29,7 +29,6 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Flags
import com.android.systemui.ambient.touch.TouchHandler.TouchSession
import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule
@@ -37,8 +36,8 @@
import com.android.systemui.ambient.touch.scrim.ScrimManager
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -63,8 +62,6 @@
private val notificationShadeWindowController: NotificationShadeWindowController,
private val valueAnimatorCreator: ValueAnimatorCreator,
private val velocityTrackerFactory: VelocityTrackerFactory,
- private val lockPatternUtils: LockPatternUtils,
- private val userTracker: UserTracker,
private val communalViewModel: CommunalViewModel,
@param:Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
private val flingAnimationUtils: FlingAnimationUtils,
@@ -75,7 +72,8 @@
@param:Named(BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE)
private val minBouncerZoneScreenPercentage: Float,
private val uiEventLogger: UiEventLogger,
- private val activityStarter: ActivityStarter
+ private val activityStarter: ActivityStarter,
+ private val keyguardInteractor: KeyguardInteractor,
) : TouchHandler {
/** An interface for creating ValueAnimators. */
interface ValueAnimatorCreator {
@@ -148,7 +146,7 @@
// If scrolling up and keyguard is not locked, dismiss both keyguard and the
// dream since there's no bouncer to show.
- if (y > e2.y && !lockPatternUtils.isSecure(userTracker.userId)) {
+ if (y > e2.y && keyguardInteractor.isKeyguardDismissible.value) {
activityStarter.executeRunnableDismissingKeyguard(
{ centralSurfaces.get().awakenDreams() },
/* cancelAction= */ null,
@@ -331,8 +329,8 @@
return
}
- // Don't set expansion if the user doesn't have a pin/password set.
- if (!lockPatternUtils.isSecure(userTracker.userId)) {
+ // Don't set expansion if keyguard is dismissible (i.e. unlocked).
+ if (keyguardInteractor.isKeyguardDismissible.value) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index 05b4656..e54dc7d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -155,7 +155,7 @@
emptyFlow()
}
}
- .collectLatest { messageViewModel -> message.value = messageViewModel }
+ .collect { messageViewModel -> message.value = messageViewModel }
}
private suspend fun listenForSimBouncerEvents() {
@@ -170,7 +170,7 @@
emptyFlow()
}
}
- .collectLatest {
+ .collect {
if (it != null) {
message.value = it
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
index 2a27271..2d57e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneActionsViewModel.kt
@@ -25,7 +25,6 @@
import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
/**
@@ -46,7 +45,7 @@
Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
)
}
- .collectLatest { actions -> setActions(actions) }
+ .collect { actions -> setActions(actions) }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index b985fc4..adc4bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -160,7 +160,7 @@
launch {
userSwitcher.selectedUser
.map { it.image.toBitmap() }
- .collectLatest { _selectedUserImage.value = it }
+ .collect { _selectedUserImage.value = it }
}
launch {
@@ -187,34 +187,32 @@
)
}
}
- .collectLatest { _userSwitcherDropdown.value = it }
+ .collect { _userSwitcherDropdown.value = it }
}
launch {
combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
- .collectLatest { _dialogViewModel.value = it }
+ .collect { _dialogViewModel.value = it }
}
- launch {
- actionButtonInteractor.actionButton.collectLatest { _actionButton.value = it }
- }
+ launch { actionButtonInteractor.actionButton.collect { _actionButton.value = it } }
launch {
authMethodViewModel
.map { authMethod -> isSideBySideSupported(authMethod) }
- .collectLatest { _isSideBySideSupported.value = it }
+ .collect { _isSideBySideSupported.value = it }
}
launch {
authMethodViewModel
.map { authMethod -> isFoldSplitRequired(authMethod) }
- .collectLatest { _isFoldSplitRequired.value = it }
+ .collect { _isFoldSplitRequired.value = it }
}
launch {
message.isLockoutMessagePresent
.map { lockoutMessagePresent -> !lockoutMessagePresent }
- .collectLatest { _isInputEnabled.value = it }
+ .collect { _isInputEnabled.value = it }
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index fc860e5..2493cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -34,7 +34,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
@@ -105,11 +104,9 @@
combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus ->
hasInput && !hasFocus
}
- .collectLatest { _isTextFieldFocusRequested.value = it }
+ .collect { _isTextFieldFocusRequested.value = it }
}
- launch {
- selectedUserInteractor.selectedUser.collectLatest { _selectedUserId.value = it }
- }
+ launch { selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it } }
launch {
// Re-fetch the currently-enabled IMEs whenever the selected user changes, and
// whenever
@@ -125,7 +122,7 @@
) { selectedUserId, _ ->
inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
}
- .collectLatest { _isImeSwitcherButtonVisible.value = it }
+ .collect { _isImeSwitcherButtonVisible.value = it }
}
awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 60ec3019..0a866b4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -34,7 +34,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
@@ -86,9 +85,7 @@
coroutineScope {
launch { super.onActivated() }
launch {
- selectedDotSet
- .map { it.toList() }
- .collectLatest { selectedDotList.value = it.toList() }
+ selectedDotSet.map { it.toList() }.collect { selectedDotList.value = it.toList() }
}
awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index db78a98..df6ca9b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -42,7 +42,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -123,7 +122,7 @@
} else {
interactor.hintedPinLength
}
- .collectLatest { _hintedPinLength.value = it }
+ .collect { _hintedPinLength.value = it }
}
launch {
combine(
@@ -135,17 +134,17 @@
isAutoConfirmEnabled = isAutoConfirmEnabled,
)
}
- .collectLatest { _backspaceButtonAppearance.value = it }
+ .collect { _backspaceButtonAppearance.value = it }
}
launch {
interactor.isAutoConfirmEnabled
.map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown }
- .collectLatest { _confirmButtonAppearance.value = it }
+ .collect { _confirmButtonAppearance.value = it }
}
launch {
interactor.isPinEnhancedPrivacyEnabled
.map { !it }
- .collectLatest { _isDigitButtonAnimationEnabled.value = it }
+ .collect { _isDigitButtonAnimationEnabled.value = it }
}
awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index 5077e8d..e04d309 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -225,7 +225,7 @@
collectProgress(transition)
} else if (transition.toScene == CommunalScenes.Communal) {
if (currentToState == KeyguardState.GLANCEABLE_HUB) {
- transitionKtfTo(transitionInteractor.getStartedFromState())
+ transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
startTransitionToGlanceableHub()
collectProgress(transition)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index c3bc24f..e3f740e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -219,16 +219,122 @@
}
};
- private final DreamOverlayStateController.Callback mExitAnimationFinishedCallback =
- new DreamOverlayStateController.Callback() {
- @Override
- public void onStateChanged() {
- if (!mStateController.areExitAnimationsRunning()) {
- mStateController.removeCallback(mExitAnimationFinishedCallback);
- resetCurrentDreamOverlayLocked();
+ /**
+ * {@link ResetHandler} protects resetting {@link DreamOverlayService} by making sure reset
+ * requests are processed before subsequent actions proceed. Requests themselves are also
+ * ordered between each other as well to ensure actions are correctly sequenced.
+ */
+ private final class ResetHandler {
+ @FunctionalInterface
+ interface Callback {
+ void onComplete();
+ }
+
+ private record Info(Callback callback, String source) {}
+
+ private final ArrayList<Info> mPendingCallbacks = new ArrayList<>();
+
+ DreamOverlayStateController.Callback mStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ process(true);
}
+ };
+
+ /**
+ * Called from places where there is no need to wait for the reset to complete. This still
+ * will defer the reset until it is okay to reset and also sequences the request with
+ * others.
+ */
+ public void reset(String source) {
+ reset(()-> {}, source);
+ }
+
+ /**
+ * Invoked to request a reset with a callback that will fire after reset if it is deferred.
+ *
+ * @return {@code true} if the reset happened immediately, {@code false} if it was deferred
+ * and will fire later, invoking the callback.
+ */
+ public boolean reset(Callback callback, String source) {
+ // Always add listener pre-emptively
+ if (mPendingCallbacks.isEmpty()) {
+ mStateController.addCallback(mStateCallback);
+ }
+
+ final Info info = new Info(callback, source);
+ mPendingCallbacks.add(info);
+ process(false);
+
+ boolean processed = !mPendingCallbacks.contains(info);
+
+ if (!processed) {
+ Log.d(TAG, "delayed resetting from: " + source);
+ }
+
+ return processed;
+ }
+
+ private void resetInternal() {
+ // This ensures the container view of the current dream is removed before
+ // the controller is potentially reset.
+ removeContainerViewFromParentLocked();
+
+ if (mStarted && mWindow != null) {
+ try {
+ mWindow.clearContentView();
+ mWindowManager.removeView(mWindow.getDecorView());
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Error removing decor view when resetting overlay", e);
}
- };
+ }
+
+ mStateController.setOverlayActive(false);
+ mStateController.setLowLightActive(false);
+ mStateController.setEntryAnimationsFinished(false);
+
+ if (mDreamOverlayContainerViewController != null) {
+ mDreamOverlayContainerViewController.destroy();
+ mDreamOverlayContainerViewController = null;
+ }
+
+ if (mTouchMonitor != null) {
+ mTouchMonitor.destroy();
+ mTouchMonitor = null;
+ }
+
+ mWindow = null;
+
+ // Always unregister the any set DreamActivity from being blocked from gestures.
+ mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER,
+ GestureInteractor.Scope.Global);
+
+ mStarted = false;
+ }
+
+ private boolean canReset() {
+ return !mStateController.areExitAnimationsRunning();
+ }
+
+ private void process(boolean fromDelayedCallback) {
+ while (canReset() && !mPendingCallbacks.isEmpty()) {
+ final Info callbackInfo = mPendingCallbacks.removeFirst();
+ resetInternal();
+ callbackInfo.callback.onComplete();
+
+ if (fromDelayedCallback) {
+ Log.d(TAG, "reset overlay (delayed) for " + callbackInfo.source);
+ }
+ }
+
+ if (mPendingCallbacks.isEmpty()) {
+ mStateController.removeCallback(mStateCallback);
+ }
+ }
+ }
+
+ private final ResetHandler mResetHandler = new ResetHandler();
private final DreamOverlayStateController mStateController;
@@ -342,10 +448,8 @@
mExecutor.execute(() -> {
setLifecycleStateLocked(Lifecycle.State.DESTROYED);
-
- resetCurrentDreamOverlayLocked();
-
mDestroyed = true;
+ mResetHandler.reset("destroying");
});
mDispatcher.onServicePreSuperOnDestroy();
@@ -385,7 +489,10 @@
// Reset the current dream overlay before starting a new one. This can happen
// when two dreams overlap (briefly, for a smoother dream transition) and both
// dreams are bound to the dream overlay service.
- resetCurrentDreamOverlayLocked();
+ if (!mResetHandler.reset(() -> onStartDream(layoutParams),
+ "starting with dream already started")) {
+ return;
+ }
}
mDreamOverlayContainerViewController =
@@ -397,7 +504,7 @@
// If we are not able to add the overlay window, reset the overlay.
if (!addOverlayWindowLocked(layoutParams)) {
- resetCurrentDreamOverlayLocked();
+ mResetHandler.reset("couldn't add window while starting");
return;
}
@@ -435,7 +542,7 @@
@Override
public void onEndDream() {
- resetCurrentDreamOverlayLocked();
+ mResetHandler.reset("ending dream");
}
@Override
@@ -566,46 +673,4 @@
Log.w(TAG, "Removing dream overlay container view parent!");
parentView.removeView(containerView);
}
-
- private void resetCurrentDreamOverlayLocked() {
- if (mStateController.areExitAnimationsRunning()) {
- mStateController.addCallback(mExitAnimationFinishedCallback);
- return;
- }
-
- // This ensures the container view of the current dream is removed before
- // the controller is potentially reset.
- removeContainerViewFromParentLocked();
-
- if (mStarted && mWindow != null) {
- try {
- mWindow.clearContentView();
- mWindowManager.removeView(mWindow.getDecorView());
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Error removing decor view when resetting overlay", e);
- }
- }
-
- mStateController.setOverlayActive(false);
- mStateController.setLowLightActive(false);
- mStateController.setEntryAnimationsFinished(false);
-
- if (mDreamOverlayContainerViewController != null) {
- mDreamOverlayContainerViewController.destroy();
- mDreamOverlayContainerViewController = null;
- }
-
- if (mTouchMonitor != null) {
- mTouchMonitor.destroy();
- mTouchMonitor = null;
- }
-
- mWindow = null;
-
- // Always unregister the any set DreamActivity from being blocked from gestures.
- mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER,
- GestureInteractor.Scope.Global);
-
- mStarted = false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index cd0b3f9..6318dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -41,7 +41,6 @@
import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import javax.inject.Inject
@@ -59,7 +58,6 @@
NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
NotificationMinimalismPrototype.token dependsOn NotificationThrottleHun.token
- NotificationsHeadsUpRefactor.token dependsOn NotificationThrottleHun.token
// SceneContainer dependencies
SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 180afb2..e89594e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -23,7 +23,7 @@
import android.view.WindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
@@ -41,7 +41,7 @@
private val activityTaskManagerService: IActivityTaskManager,
private val keyguardStateController: KeyguardStateController,
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
- private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
) {
/**
@@ -148,7 +148,7 @@
// a transition to GONE. This transition needs to start even if we're not provided an app
// animation target - it's possible the app is destroyed on creation, etc. but we'll still
// be unlocking.
- keyguardTransitionInteractor.startDismissKeyguardTransition(
+ keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
reason = "Going away remote animation started"
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 1042ae3..e4b0f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -35,7 +35,6 @@
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -49,7 +48,6 @@
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
-@ExperimentalCoroutinesApi
@SysUISingleton
class FromAlternateBouncerTransitionInteractor
@Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 6e04133..4cf9ec8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -89,7 +89,7 @@
.filterRelevantKeyguardStateAnd { wakefulness -> wakefulness.isAwake() }
.debounce(50L)
.sample(
- startedKeyguardTransitionStep,
+ transitionInteractor.startedKeyguardTransitionStep,
wakeToGoneInteractor.canWakeDirectlyToGone,
)
.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 4666430..2434b29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -117,7 +117,7 @@
if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.primaryBouncerShowing
- .sample(startedKeyguardTransitionStep, ::Pair)
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isBouncerShowing, lastStartedTransitionStep) = pair
if (
@@ -132,7 +132,7 @@
fun startToLockscreenOrGlanceableHubTransition(openHub: Boolean) {
scope.launch {
if (
- transitionInteractor.startedKeyguardState.replayCache.last() ==
+ transitionInteractor.startedKeyguardTransitionStep.value.to ==
KeyguardState.DREAMING
) {
if (powerInteractor.detailedWakefulness.value.isAwake()) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index fc70ea5..228e01e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -134,16 +134,12 @@
.filterRelevantKeyguardState()
.sampleCombine(
internalTransitionInteractor.currentTransitionInfoInternal,
- finishedKeyguardState,
+ transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
keyguardInteractor.isActiveDreamLockscreenHosted,
)
.collect {
- (
- isAbleToDream,
- transitionInfo,
- finishedKeyguardState,
- isActiveDreamLockscreenHosted) ->
- val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
+ (isAbleToDream, transitionInfo, isOnLockscreen, isActiveDreamLockscreenHosted)
+ ->
val isTransitionInterruptible =
transitionInfo.to == KeyguardState.LOCKSCREEN &&
!invalidFromStates.contains(transitionInfo.from)
@@ -189,7 +185,7 @@
scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
shadeRepository.legacyShadeExpansion
.sampleCombine(
- startedKeyguardTransitionStep,
+ transitionInteractor.startedKeyguardTransitionStep,
internalTransitionInteractor.currentTransitionInfoInternal,
keyguardInteractor.statusBarState,
keyguardInteractor.isKeyguardDismissible,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index f9ab1bb..bde0f56 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -62,16 +62,16 @@
communalInteractor
.transitionProgressToScene(toScene)
.sample(
- transitionInteractor.startedKeyguardState,
+ transitionInteractor.startedKeyguardTransitionStep,
::Pair,
)
- .collect { (transitionProgress, lastStartedState) ->
+ .collect { (transitionProgress, lastStartedStep) ->
val id = transitionId
if (id == null) {
// No transition started.
if (
transitionProgress is CommunalTransitionProgressModel.Transition &&
- lastStartedState == fromState
+ lastStartedStep.to == fromState
) {
transitionId =
transitionRepository.startTransition(
@@ -84,7 +84,7 @@
)
}
} else {
- if (lastStartedState != toState) {
+ if (lastStartedStep.to != toState) {
return@collect
}
// An existing `id` means a transition is started, and calls to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
new file mode 100644
index 0000000..c19bbbc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import javax.inject.Inject
+
+@SysUISingleton
+class KeyguardDismissTransitionInteractor
+@Inject
+constructor(
+ private val repository: KeyguardTransitionRepository,
+ private val fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor,
+ private val fromPrimaryBouncerTransitionInteractor: FromPrimaryBouncerTransitionInteractor,
+ private val fromAodTransitionInteractor: FromAodTransitionInteractor,
+ private val fromAlternateBouncerTransitionInteractor: FromAlternateBouncerTransitionInteractor,
+ private val fromDozingTransitionInteractor: FromDozingTransitionInteractor,
+ private val fromOccludedTransitionInteractor: FromOccludedTransitionInteractor,
+) {
+
+ /**
+ * Called to start a transition that will ultimately dismiss the keyguard from the current
+ * state.
+ *
+ * This is called exclusively by sources that can authoritatively say we should be unlocked,
+ * including KeyguardSecurityContainerController and WindowManager.
+ */
+ fun startDismissKeyguardTransition(reason: String = "") {
+ if (SceneContainerFlag.isEnabled) return
+ Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
+ when (val startedState = repository.currentTransitionInfoInternal.value.to) {
+ LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard()
+ PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer()
+ ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer()
+ AOD -> fromAodTransitionInteractor.dismissAod()
+ DOZING -> fromDozingTransitionInteractor.dismissFromDozing()
+ KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.dismissFromOccluded()
+ KeyguardState.GONE ->
+ Log.i(
+ TAG,
+ "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
+ )
+ else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
+ }
+ }
+
+ companion object {
+ private val TAG = KeyguardDismissTransitionInteractor::class.simpleName
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 4aef808..44aafab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -48,7 +48,7 @@
@Application scope: CoroutineScope,
val repository: KeyguardRepository,
val biometricSettingsRepository: BiometricSettingsRepository,
- transitionInteractor: KeyguardTransitionInteractor,
+ keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
internalTransitionInteractor: InternalKeyguardTransitionInteractor,
) {
@@ -94,7 +94,9 @@
showKeyguardWhenReenabled
.filter { shouldDismiss -> shouldDismiss }
.collect {
- transitionInteractor.startDismissKeyguardTransition("keyguard disabled")
+ keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+ "keyguard disabled"
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index 4a8ada7..505c749d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -48,7 +48,6 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -74,9 +73,7 @@
val isLongPressHandlingEnabled: StateFlow<Boolean> =
if (isFeatureEnabled()) {
combine(
- transitionInteractor.finishedKeyguardState.map {
- it == KeyguardState.LOCKSCREEN
- },
+ transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
repository.isQuickSettingsVisible,
) { isFullyTransitionedToLockScreen, isQuickSettingsVisible ->
isFullyTransitionedToLockScreen && !isQuickSettingsVisible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index c10bacf..d11a41e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -25,14 +25,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -49,7 +42,6 @@
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -58,7 +50,6 @@
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.launch
/** Encapsulates business-logic related to the keyguard transitions. */
@@ -69,14 +60,6 @@
constructor(
@Application val scope: CoroutineScope,
private val repository: KeyguardTransitionRepository,
- private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
- private val fromPrimaryBouncerTransitionInteractor:
- dagger.Lazy<FromPrimaryBouncerTransitionInteractor>,
- private val fromAodTransitionInteractor: dagger.Lazy<FromAodTransitionInteractor>,
- private val fromAlternateBouncerTransitionInteractor:
- dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
- private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
- private val fromOccludedTransitionInteractor: dagger.Lazy<FromOccludedTransitionInteractor>,
private val sceneInteractor: SceneInteractor,
) {
private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
@@ -286,67 +269,10 @@
}
/** The last [TransitionStep] with a [TransitionState] of STARTED */
- val startedKeyguardTransitionStep: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
-
- /** The destination state of the last [TransitionState.STARTED] transition. */
- @SuppressLint("SharedFlowCreation")
- val startedKeyguardState: SharedFlow<KeyguardState> =
- startedKeyguardTransitionStep
- .map { step -> step.to }
- .buffer(2, BufferOverflow.DROP_OLDEST)
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
-
- /** The from state of the last [TransitionState.STARTED] transition. */
- // TODO: is it performant to have several SharedFlows side by side instead of one?
- @SuppressLint("SharedFlowCreation")
- val startedKeyguardFromState: SharedFlow<KeyguardState> =
- startedKeyguardTransitionStep
- .map { step -> step.from }
- .buffer(2, BufferOverflow.DROP_OLDEST)
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
-
- /**
- * The last [KeyguardState] to which we [TransitionState.FINISHED] a transition.
- *
- * WARNING: This will NOT emit a value if a transition is CANCELED, and will also not emit a
- * value when a subsequent transition is STARTED. It will *only* emit once we have finally
- * FINISHED in a state. This can have unintuitive implications.
- *
- * For example, if we're transitioning from GONE -> DOZING, and that transition is CANCELED in
- * favor of a DOZING -> LOCKSCREEN transition, the FINISHED state is still GONE, and will remain
- * GONE throughout the DOZING -> LOCKSCREEN transition until the DOZING -> LOCKSCREEN transition
- * finishes (at which point we'll be FINISHED in LOCKSCREEN).
- *
- * Since there's no real limit to how many consecutive transitions can be canceled, it's even
- * possible for the FINISHED state to be the same as the STARTED state while still
- * transitioning.
- *
- * For example:
- * 1. We're finished in GONE.
- * 2. The user presses the power button, starting a GONE -> DOZING transition. We're still
- * FINISHED in GONE.
- * 3. The user changes their mind, pressing the power button to wake up; this starts a DOZING ->
- * LOCKSCREEN transition. We're still FINISHED in GONE.
- * 4. The user quickly swipes away the lockscreen prior to DOZING -> LOCKSCREEN finishing; this
- * starts a LOCKSCREEN -> GONE transition. We're still FINISHED in GONE, but we've also
- * STARTED a transition *to* GONE.
- * 5. We'll emit KeyguardState.GONE again once the transition finishes.
- *
- * If you just need to know when we eventually settle into a state, this flow is likely
- * sufficient. However, if you're having issues with state *during* transitions started after
- * one or more canceled transitions, you probably need to use [currentKeyguardState].
- */
- @SuppressLint("SharedFlowCreation")
- val finishedKeyguardState: SharedFlow<KeyguardState> =
+ val startedKeyguardTransitionStep: StateFlow<TransitionStep> =
repository.transitions
- .transform { step ->
- if (step.transitionState == TransitionState.FINISHED) {
- emit(step.to)
- }
- }
- .buffer(2, BufferOverflow.DROP_OLDEST)
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+ .filter { step -> step.transitionState == TransitionState.STARTED }
+ .stateIn(scope, SharingStarted.Eagerly, TransitionStep())
/**
* The [KeyguardState] we're currently in.
@@ -412,7 +338,6 @@
it.from
}
}
- .distinctUntilChanged()
.stateIn(scope, SharingStarted.Eagerly, OFF)
val isInTransition =
@@ -425,33 +350,6 @@
}
/**
- * Called to start a transition that will ultimately dismiss the keyguard from the current
- * state.
- *
- * This is called exclusively by sources that can authoritatively say we should be unlocked,
- * including KeyguardSecurityContainerController and WindowManager.
- */
- fun startDismissKeyguardTransition(reason: String = "") {
- if (SceneContainerFlag.isEnabled) return
- Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
- when (val startedState = repository.currentTransitionInfoInternal.value.to) {
- LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
- PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
- ALTERNATE_BOUNCER ->
- fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
- AOD -> fromAodTransitionInteractor.get().dismissAod()
- DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
- OCCLUDED -> fromOccludedTransitionInteractor.get().dismissFromOccluded()
- GONE ->
- Log.i(
- TAG,
- "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
- )
- else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
- }
- }
-
- /**
* Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet
* completed it.
*
@@ -509,12 +407,13 @@
fun isFinishedIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> {
return if (SceneContainerFlag.isEnabled) {
- sceneInteractor.transitionState
- .map { it.isIdle(scene) || it.isTransitioning(from = scene) }
- .distinctUntilChanged()
- } else {
- isFinishedIn(stateWithoutSceneContainer)
- }
+ sceneInteractor.transitionState.map {
+ it.isIdle(scene) || it.isTransitioning(from = scene)
+ }
+ } else {
+ isFinishedIn(stateWithoutSceneContainer)
+ }
+ .distinctUntilChanged()
}
/** Whether we've FINISHED a transition to a state */
@@ -523,17 +422,26 @@
return finishedKeyguardState.map { it == state }.distinctUntilChanged()
}
+ fun isCurrentlyIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> {
+ return if (SceneContainerFlag.isEnabled) {
+ // In STL there is no difference between finished/currentState
+ isFinishedIn(scene, stateWithoutSceneContainer)
+ } else {
+ stateWithoutSceneContainer.checkValidState()
+ currentKeyguardState.map { it == stateWithoutSceneContainer }
+ }
+ .distinctUntilChanged()
+ }
+
fun getCurrentState(): KeyguardState {
return currentKeyguardState.replayCache.last()
}
- fun getStartedFromState(): KeyguardState {
- return startedKeyguardFromState.replayCache.last()
- }
-
- fun getFinishedState(): KeyguardState {
- return finishedKeyguardState.replayCache.last()
- }
+ private val finishedKeyguardState: StateFlow<KeyguardState> =
+ repository.transitions
+ .filter { it.transitionState == TransitionState.FINISHED }
+ .map { it.to }
+ .stateIn(scope, SharingStarted.Eagerly, OFF)
companion object {
private val TAG = KeyguardTransitionInteractor::class.simpleName
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index f0bf402..9b8d9ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -37,6 +37,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAwakeInState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.settings.SecureSettings
@@ -181,14 +182,20 @@
scope.launch {
powerInteractor.detailedWakefulness
.distinctUntilChangedBy { it.isAwake() }
- .sample(transitionInteractor.currentKeyguardState, ::Pair)
- .collect { (wakefulness, currentState) ->
+ .sample(
+ transitionInteractor.isCurrentlyIn(
+ Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
+ ::Pair
+ )
+ .collect { (wakefulness, finishedInGone) ->
// Save isAwake for use in onDreamingStarted/onDreamingStopped.
this@KeyguardWakeDirectlyToGoneInteractor.isAwake = wakefulness.isAwake()
// If we're sleeping from GONE, check the timeout and lock instantly settings.
// These are not relevant if we're coming from non-GONE states.
- if (!isAwake && currentState == KeyguardState.GONE) {
+ if (!isAwake && finishedInGone) {
val lockTimeoutDuration = getCanIgnoreAuthAndReturnToGoneDuration()
// If the screen timed out and went to sleep, and the lock timeout is > 0ms,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
index 47818cb..e00e33d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
@@ -45,6 +45,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -76,7 +77,7 @@
private val disableFlagsForUserId =
combine(
selectedUserInteractor.selectedUser,
- keyguardTransitionInteractor.startedKeyguardState,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to },
deviceConfigInteractor.property(
namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
index 906d586..e404f27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
@@ -22,12 +22,12 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import javax.inject.Inject
/**
* Handles logic around the swipe to dismiss gesture, where the user swipes up on the dismissable
@@ -53,15 +53,15 @@
val dismissFling =
shadeRepository.currentFling
.sample(
- transitionInteractor.startedKeyguardState,
+ transitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardDismissible,
keyguardInteractor.statusBarState,
)
- .filter { (flingInfo, startedState, keyguardDismissable, statusBarState) ->
+ .filter { (flingInfo, startedStep, keyguardDismissable, statusBarState) ->
flingInfo != null &&
- !flingInfo.expand &&
- statusBarState != StatusBarState.SHADE_LOCKED &&
- startedState == KeyguardState.LOCKSCREEN &&
+ !flingInfo.expand &&
+ statusBarState != StatusBarState.SHADE_LOCKED &&
+ startedStep.to == KeyguardState.LOCKSCREEN &&
keyguardDismissable
}
.map { (flingInfo, _) -> flingInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 950eafa..ba12e93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -32,7 +32,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -62,17 +61,6 @@
abstract fun start()
- /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because
- * the [sample] utility internally runs a collect on the Unconfined dispatcher, resulting
- * in continuations on the main thread. We don't want that for classes that inherit from this.
- */
- val startedKeyguardTransitionStep =
- transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher)
- // The following are MutableSharedFlows, and do not require flowOn
- val startedKeyguardState = transitionInteractor.startedKeyguardState
- val finishedKeyguardState = transitionInteractor.finishedKeyguardState
- val currentKeyguardState = transitionInteractor.currentKeyguardState
-
suspend fun startTransitionTo(
toState: KeyguardState,
animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
@@ -92,17 +80,6 @@
" $fromState. This should never happen - check currentTransitionInfoInternal" +
" or use filterRelevantKeyguardState before starting transitions."
)
-
- if (fromState == transitionInteractor.finishedKeyguardState.replayCache.last()) {
- Log.e(
- name,
- "This transition would not have been ignored prior to ag/26681239, since we " +
- "are FINISHED in $fromState (but have since started another transition). " +
- "If ignoring this transition has caused a regression, fix it by ensuring " +
- "that transitions are exclusively started from the most recently started " +
- "state."
- )
- }
return null
}
@@ -207,7 +184,7 @@
powerInteractor.isAsleep
.filter { isAsleep -> isAsleep }
.filterRelevantKeyguardState()
- .sample(startedKeyguardTransitionStep)
+ .sample(transitionInteractor.startedKeyguardTransitionStep)
.map(modeOnCanceledFromStartedStep)
.collect { modeOnCanceled ->
startTransitionTo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 25b2b7c..ac87400 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -63,10 +63,13 @@
) {
private val defaultSurfaceBehindVisibility =
combine(
- transitionInteractor.finishedKeyguardState,
+ transitionInteractor.isFinishedIn(
+ scene = Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
wakeToGoneInteractor.canWakeDirectlyToGone,
- ) { finishedState, canWakeDirectlyToGone ->
- isSurfaceVisible(finishedState) || canWakeDirectlyToGone
+ ) { isOnGone, canWakeDirectlyToGone ->
+ isOnGone || canWakeDirectlyToGone
}
/**
@@ -196,18 +199,20 @@
edge = Edge.create(to = Scenes.Gone),
edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE)
),
- transitionInteractor.finishedKeyguardState,
+ transitionInteractor.isFinishedIn(
+ scene = Scenes.Gone,
+ stateWithoutSceneContainer = KeyguardState.GONE
+ ),
surfaceBehindInteractor.isAnimatingSurface,
notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
- ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
+ ) { isInTransitionToGone, isOnGone, isAnimatingSurface, notifLaunchRunning ->
// Using the animation if we're animating it directly, or if the
// ActivityLaunchAnimator is in the process of animating it.
val animationsRunning = isAnimatingSurface || notifLaunchRunning
// We may still be animating the surface after the keyguard is fully GONE, since
// some animations (like the translation spring) are not tied directly to the
// transition step amount.
- isInTransitionToGone ||
- (finishedState == KeyguardState.GONE && animationsRunning)
+ isInTransitionToGone || (isOnGone && animationsRunning)
}
.distinctUntilChanged()
}
@@ -248,7 +253,7 @@
// transition. Same for waking directly to gone, due to the lockscreen being
// disabled or because the device was woken back up before the lock timeout
// duration elapsed.
- KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+ false
} else if (canWakeDirectlyToGone) {
// Never show the lockscreen if we can wake directly to GONE. This means
// that the lock timeout has not yet elapsed, or the keyguard is disabled.
@@ -274,8 +279,7 @@
// *not* play the going away animation or related animations.
false
} else {
- // Otherwise, use the visibility of the current state.
- KeyguardState.lockscreenVisibleInState(currentState)
+ currentState != KeyguardState.GONE
}
}
.distinctUntilChanged()
@@ -302,10 +306,4 @@
!BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
}
.distinctUntilChanged()
-
- companion object {
- fun isSurfaceVisible(state: KeyguardState): Boolean {
- return !KeyguardState.lockscreenVisibleInState(state)
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index b850095..ffd7812 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -116,7 +116,7 @@
} else {
val targetState =
if (idle.currentScene == Scenes.Lockscreen) {
- transitionInteractor.getStartedFromState()
+ transitionInteractor.startedKeyguardTransitionStep.value.from
} else {
UNDEFINED
}
@@ -155,7 +155,7 @@
val currentToState =
internalTransitionInteractor.currentTransitionInfoInternal.value.to
if (currentToState == UNDEFINED) {
- transitionKtfTo(transitionInteractor.getStartedFromState())
+ transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
}
startTransitionFromLockscreen()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 24db3c2..080ddfd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -156,12 +156,6 @@
companion object {
- /** Whether the lockscreen is visible when we're FINISHED in the given state. */
- fun lockscreenVisibleInState(state: KeyguardState): Boolean {
- // TODO(b/349784682): Transform deprecated states for Flexiglass
- return state != GONE
- }
-
/**
* Whether the device is awake ([PowerInteractor.isAwake]) when we're FINISHED in the given
* keyguard state.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 0032c2f..e2ad4635 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -46,6 +46,7 @@
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludedToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewModel
@@ -205,6 +206,12 @@
@Binds
@IntoSet
+ abstract fun occludedToDozing(
+ impl: OccludedToDozingTransitionViewModel
+ ): DeviceEntryIconTransition
+
+ @Binds
+ @IntoSet
abstract fun occludedToLockscreen(
impl: OccludedToLockscreenTransitionViewModel
): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 6f8389f..9f68210 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -59,6 +59,7 @@
alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel,
dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel,
primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
+ occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel,
) {
val color: Flow<Int> =
deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground ->
@@ -103,6 +104,7 @@
dreamingToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
primaryBouncerToLockscreenTransitionViewModel
.deviceEntryBackgroundViewAlpha,
+ occludedToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
)
.merge()
.onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 06b76b3..87c32a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -53,10 +53,10 @@
) {
private val isShowingAodOrDozing: Flow<Boolean> =
combine(
- transitionInteractor.startedKeyguardState,
+ transitionInteractor.startedKeyguardTransitionStep,
transitionInteractor.transitionValue(KeyguardState.DOZING),
- ) { startedKeyguardState, dozingTransitionValue ->
- startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f
+ ) { startedKeyguardStep, dozingTransitionValue ->
+ startedKeyguardStep.to == KeyguardState.AOD || dozingTransitionValue == 1f
}
private fun getColor(usingBackgroundProtection: Boolean): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 5ce1b5e..d3bb4f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -83,8 +83,8 @@
private val intEvaluator = IntEvaluator()
private val floatEvaluator = FloatEvaluator()
private val showingAlternateBouncer: Flow<Boolean> =
- transitionInteractor.startedKeyguardState.map { keyguardState ->
- keyguardState == KeyguardState.ALTERNATE_BOUNCER
+ transitionInteractor.startedKeyguardTransitionStep.map { keyguardStep ->
+ keyguardStep.to == KeyguardState.ALTERNATE_BOUNCER
}
private val qsProgress: Flow<Float> = shadeInteractor.qsExpansion.onStart { emit(0f) }
private val shadeExpansion: Flow<Float> = shadeInteractor.shadeExpansion.onStart { emit(0f) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index c885c9a..fe4ebfe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -85,9 +85,7 @@
private val previewMode = MutableStateFlow(PreviewMode())
private val showingLockscreen: Flow<Boolean> =
- transitionInteractor.finishedKeyguardState.map { keyguardState ->
- keyguardState == KeyguardState.LOCKSCREEN
- }
+ transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN)
/** The only time the expansion is important is while lockscreen is actively displayed */
private val shadeExpansionAlpha =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index a96869d..ebdcaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -132,8 +132,8 @@
val burnInModel = _burnInModel.asStateFlow()
val burnInLayerVisibility: Flow<Int> =
- keyguardTransitionInteractor.startedKeyguardState
- .filter { it == AOD || it == LOCKSCREEN }
+ keyguardTransitionInteractor.startedKeyguardTransitionStep
+ .filter { it.to == AOD || it.to == LOCKSCREEN }
.map { VISIBLE }
val goneToAodTransition =
@@ -333,16 +333,17 @@
.transitionValue(LOCKSCREEN)
.map { it > 0f }
.onStart { emit(false) },
- keyguardTransitionInteractor.finishedKeyguardState.map {
- KeyguardState.lockscreenVisibleInState(it)
- },
+ keyguardTransitionInteractor.isFinishedIn(
+ scene = Scenes.Gone,
+ stateWithoutSceneContainer = GONE
+ ),
deviceEntryInteractor.isBypassEnabled,
areNotifsFullyHiddenAnimated(),
isPulseExpandingAnimated(),
) { flows ->
val goneToAodTransitionRunning = flows[0] as Boolean
val isOnLockscreen = flows[1] as Boolean
- val onKeyguard = flows[2] as Boolean
+ val isOnGone = flows[2] as Boolean
val isBypassEnabled = flows[3] as Boolean
val notifsFullyHidden = flows[4] as AnimatedValue<Boolean>
val pulseExpanding = flows[5] as AnimatedValue<Boolean>
@@ -352,8 +353,7 @@
// animation is playing, in which case we want them to be visible if we're
// animating in the AOD UI and will be switching to KEYGUARD shortly.
goneToAodTransitionRunning ||
- (!onKeyguard &&
- !screenOffAnimationController.shouldShowAodIconsWhenShade()) ->
+ (isOnGone && !screenOffAnimationController.shouldShowAodIconsWhenShade()) ->
AnimatedValue.NotAnimating(false)
else ->
zip(notifsFullyHidden, pulseExpanding) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index fcafd5e..adb63b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -40,7 +40,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
@@ -91,13 +90,13 @@
end = end,
)
}
- .collectLatest { _unfoldTranslations.value = it }
+ .collect { _unfoldTranslations.value = it }
}
launch {
occlusionInteractor.isOccludingActivityShown
.map { !it }
- .collectLatest { _isContentVisible.value = it }
+ .collect { _isContentVisible.value = it }
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
index 7383f57..2819e61 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
@@ -35,7 +35,6 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -100,7 +99,7 @@
}
}
}
- .collectLatest { setActions(it) }
+ .collect { setActions(it) }
}
private fun swipeDownFromTop(pointerCount: Int): Swipe {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
index af01930..4fb2b9b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -17,15 +17,19 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
/**
* Breaks down OCCLUDED->DOZING transition into discrete steps for corresponding views to consume.
@@ -35,8 +39,9 @@
class OccludedToDozingTransitionViewModel
@Inject
constructor(
+ deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
-) {
+) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION,
@@ -50,4 +55,17 @@
duration = 250.milliseconds,
onStep = { it },
)
+
+ val deviceEntryBackgroundViewAlpha: Flow<Float> =
+ transitionAnimation.immediatelyTransitionTo(0f)
+
+ override val deviceEntryParentViewAlpha: Flow<Float> =
+ deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
+ ->
+ if (udfpsEnrolledAndEnabled) {
+ transitionAnimation.immediatelyTransitionTo(1f)
+ } else {
+ emptyFlow()
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
index e45d537..708b408 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
@@ -34,7 +34,9 @@
) {
/** When the last keyguard state transition started, was the shade fully expanded? */
private val lastStartedTransitionHadShadeFullyExpanded: Flow<Boolean> =
- transitionInteractor.startedKeyguardState.sample(shadeInteractor.isAnyFullyExpanded)
+ transitionInteractor.startedKeyguardTransitionStep.sample(
+ shadeInteractor.isAnyFullyExpanded
+ )
/**
* Decide which flow to use depending on the shade expansion state at the start of the last
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 f5a7966..bf9ef8c 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
@@ -49,7 +49,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -105,12 +104,14 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -332,6 +333,11 @@
private val controllerById = mutableMapOf<String, MediaViewController>()
private val commonViewModels = mutableListOf<MediaCommonViewModel>()
+ private val isOnGone =
+ keyguardTransitionInteractor
+ .isFinishedIn(Scenes.Gone, GONE)
+ .stateIn(applicationScope, SharingStarted.Eagerly, true)
+
init {
dumpManager.registerDumpable(TAG, this)
mediaFrame = inflateMediaCarousel()
@@ -913,9 +919,7 @@
if (SceneContainerFlag.isEnabled) {
!deviceEntryInteractor.isDeviceEntered.value
} else {
- KeyguardState.lockscreenVisibleInState(
- keyguardTransitionInteractor.getFinishedState()
- )
+ !isOnGone.value
}
return !allowMediaPlayerOnLockScreen && isOnLockscreen
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
index af55f5a..2bb5dc66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneActionsViewModel.kt
@@ -31,7 +31,6 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -78,7 +77,7 @@
}
}
}
- .collectLatest { actions -> setActions(actions) }
+ .collect { actions -> setActions(actions) }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
index d2967b8..9956a46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
@@ -29,7 +29,6 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
/**
@@ -62,7 +61,7 @@
}
}
}
- .collectLatest { actions -> setActions(actions) }
+ .collect { actions -> setActions(actions) }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 6c63c97..751448f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -27,7 +27,7 @@
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.ComposeLockscreen
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
import com.android.systemui.statusbar.phone.PredictiveBackSysUiFlag
/** Helper for reading or using the scene container flag state. */
@@ -43,7 +43,7 @@
KeyguardBottomAreaRefactor.isEnabled &&
KeyguardWmStateRefactor.isEnabled &&
MigrateClocksToBlueprint.isEnabled &&
- NotificationsHeadsUpRefactor.isEnabled &&
+ NotificationThrottleHun.isEnabled &&
PredictiveBackSysUiFlag.isEnabled &&
DeviceEntryUdfpsRefactor.isEnabled
@@ -59,7 +59,7 @@
KeyguardBottomAreaRefactor.token,
KeyguardWmStateRefactor.token,
MigrateClocksToBlueprint.token,
- NotificationsHeadsUpRefactor.token,
+ NotificationThrottleHun.token,
PredictiveBackSysUiFlag.token,
DeviceEntryUdfpsRefactor.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
index b707a5a..88d4c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
@@ -29,7 +29,6 @@
import com.android.systemui.shade.shared.model.ShadeMode
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
class GoneSceneActionsViewModel
@@ -82,7 +81,7 @@
}
}
}
- .collectLatest { setActions(it) }
+ .collect { setActions(it) }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 4639e22..3bb494b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -203,6 +203,13 @@
*/
private var shadeConsumingTouches = false
+ /**
+ * True if the shade is showing at all.
+ *
+ * Inverse of [ShadeInteractor.isShadeFullyCollapsed]
+ */
+ private var shadeShowing = false
+
/** True if the keyguard transition state is finished on [KeyguardState.LOCKSCREEN]. */
private var onLockscreen = false
@@ -414,6 +421,7 @@
),
{ (isFullyExpanded, isUserInteracting, isShadeFullyCollapsed) ->
shadeConsumingTouches = isUserInteracting
+ shadeShowing = !isShadeFullyCollapsed
val expandedAndNotInteractive = isFullyExpanded && !isUserInteracting
// If we ever are fully expanded and not interacting, capture this state as we
@@ -529,7 +537,7 @@
val isMove = ev.actionMasked == MotionEvent.ACTION_MOVE
val isCancel = ev.actionMasked == MotionEvent.ACTION_CANCEL
- val hubOccluded = anyBouncerShowing || shadeShowingAndConsumingTouches
+ val hubOccluded = anyBouncerShowing || shadeConsumingTouches || shadeShowing
if ((isDown || isMove) && !hubOccluded) {
if (isDown) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index c023b83..31813b2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -191,12 +191,10 @@
import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -439,7 +437,6 @@
private boolean mExpandingFromHeadsUp;
private boolean mCollapsedOnDown;
private boolean mClosingWithAlphaFadeOut;
- private boolean mHeadsUpVisible;
private boolean mHeadsUpAnimatingAway;
private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
@@ -610,7 +607,6 @@
private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
- private final HeadsUpNotificationInteractor mHeadsUpNotificationInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final PowerInteractor mPowerInteractor;
@@ -776,7 +772,6 @@
ActivityStarter activityStarter,
SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
ActiveNotificationsInteractor activeNotificationsInteractor,
- HeadsUpNotificationInteractor headsUpNotificationInteractor,
ShadeAnimationInteractor shadeAnimationInteractor,
KeyguardViewConfigurator keyguardViewConfigurator,
DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
@@ -811,7 +806,6 @@
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
mActiveNotificationsInteractor = activeNotificationsInteractor;
- mHeadsUpNotificationInteractor = headsUpNotificationInteractor;
mKeyguardInteractor = keyguardInteractor;
mPowerInteractor = powerInteractor;
mKeyguardViewConfigurator = keyguardViewConfigurator;
@@ -1222,11 +1216,6 @@
}
},
mMainDispatcher);
-
- if (NotificationsHeadsUpRefactor.isEnabled()) {
- collectFlow(mView, mHeadsUpNotificationInteractor.isHeadsUpOrAnimatingAway(),
- setHeadsUpVisible(), mMainDispatcher);
- }
}
@VisibleForTesting
@@ -3077,21 +3066,7 @@
mPanelAlphaEndAction = r;
}
- private Consumer<Boolean> setHeadsUpVisible() {
- return (Boolean isHeadsUpVisible) -> {
- mHeadsUpVisible = isHeadsUpVisible;
-
- if (isHeadsUpVisible) {
- updateNotificationTranslucency();
- }
- updateExpansionAndVisibility();
- updateGestureExclusionRect();
- mKeyguardStatusBarViewController.updateForHeadsUp();
- };
- }
-
private void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
mHeadsUpAnimatingAway = headsUpAnimatingAway;
mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
updateVisibility();
@@ -3107,16 +3082,13 @@
}
private boolean shouldPanelBeVisible() {
- boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible
- : (mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
+ boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode;
return headsUpVisible || isExpanded() || mBouncerShowing;
}
private void setHeadsUpManager(HeadsUpManager headsUpManager) {
mHeadsUpManager = headsUpManager;
- if (!NotificationsHeadsUpRefactor.isEnabled()) {
- mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
- }
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(
headsUpManager,
mStatusBarService,
@@ -3204,8 +3176,7 @@
}
private boolean isPanelVisibleBecauseOfHeadsUp() {
- boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible
- : (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway);
+ boolean headsUpVisible = mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
return headsUpVisible && mBarState == StatusBarState.SHADE;
}
@@ -3521,7 +3492,6 @@
ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
- ipw.print("mHeadsUpVisible="); ipw.println(mHeadsUpVisible);
ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding);
@@ -4446,8 +4416,6 @@
private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener {
@Override
public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
-
if (inPinnedMode) {
mHeadsUpExistenceChangedRunnable.run();
updateNotificationTranslucency();
@@ -4464,8 +4432,6 @@
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
-
if (!isKeyguardShowing()) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true);
}
@@ -4473,8 +4439,6 @@
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
-
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the
// notification
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
index f270e82..9c4bf1f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -26,6 +26,7 @@
import com.android.systemui.util.kotlin.BooleanFlowOperators.any
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
/** Models UI state for the shade window. */
@SysUISingleton
@@ -42,9 +43,11 @@
val isKeyguardOccluded: Flow<Boolean> =
listOf(
// Finished in state...
- keyguardTransitionInteractor.isFinishedIn(OCCLUDED),
- keyguardTransitionInteractor.isFinishedIn(DREAMING),
- keyguardTransitionInteractor.isFinishedIn(Scenes.Communal, GLANCEABLE_HUB),
+ keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
+ keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f },
+ keyguardTransitionInteractor.transitionValue(Scenes.Communal, GLANCEABLE_HUB).map {
+ it == 1f
+ },
// ... or transitions between those states
keyguardTransitionInteractor.isInTransition(Edge.create(OCCLUDED, DREAMING)),
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
index c210fc5..abf1f4c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
@@ -28,7 +28,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
/**
* Models UI state and handles user input for the overlay shade UI, which shows a shade as an
@@ -48,7 +47,7 @@
val panelAlignment = shadeInteractor.shadeAlignment
override suspend fun onActivated(): Nothing {
- sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collectLatest { sceneKey ->
+ sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collect { sceneKey ->
_backgroundScene.value = sceneKey
}
awaitCancellation()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index c8abb1f..a154e91 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -46,7 +46,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -132,12 +131,10 @@
launch {
mobileIconsInteractor.filteredSubscriptions
.map { list -> list.map { it.subscriptionId } }
- .collectLatest { _mobileSubIds.value = it }
+ .collect { _mobileSubIds.value = it }
}
- launch {
- shadeInteractor.isQsEnabled.map { !it }.collectLatest { _isDisabled.value = it }
- }
+ launch { shadeInteractor.isQsEnabled.map { !it }.collect { _isDisabled.value = it } }
awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
index bdc0fdb..ab71913 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
@@ -29,7 +29,6 @@
import com.android.systemui.shade.shared.model.ShadeMode
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
/**
@@ -67,7 +66,7 @@
}
}
}
- .collectLatest { actions -> setActions(actions) }
+ .collect { actions -> setActions(actions) }
}
@AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
index 7a79a22..7c70759 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
@@ -35,12 +35,10 @@
import dagger.assisted.AssistedInject
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
/**
* Models UI state used to render the content of the shade scene.
@@ -75,11 +73,9 @@
private val footerActionsControllerInitialized = AtomicBoolean(false)
override suspend fun onActivated(): Nothing {
- deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered ->
+ deviceEntryInteractor.isDeviceEntered.collect { isDeviceEntered ->
_isEmptySpaceClickable.value = !isDeviceEntered
}
-
- awaitCancellation()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
index 173ff37..be733d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -16,14 +16,24 @@
package com.android.systemui.statusbar.chips
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
+import dagger.Binds
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
@Module
abstract class StatusBarChipsModule {
+ @Binds
+ @IntoMap
+ @ClassKey(DemoRonChipViewModel::class)
+ abstract fun binds(impl: DemoRonChipViewModel): CoreStartable
+
companion object {
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 18ea0b4..e825258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -69,7 +69,7 @@
state.notificationIconView
)
} else {
- OngoingActivityChipModel.ChipIcon.Basic(phoneIcon)
+ OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
}
// This block mimics OngoingCallController#updateChip.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index cf4e707..d4ad6ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -190,7 +190,7 @@
): OngoingActivityChipModel.Shown {
return OngoingActivityChipModel.Shown.Timer(
icon =
- OngoingActivityChipModel.ChipIcon.Basic(
+ OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
CAST_TO_OTHER_DEVICE_ICON,
// This string is "Casting screen"
@@ -215,7 +215,7 @@
private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Shown {
return OngoingActivityChipModel.Shown.IconOnly(
icon =
- OngoingActivityChipModel.ChipIcon.Basic(
+ OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
CAST_TO_OTHER_DEVICE_ICON,
// This string is just "Casting"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
new file mode 100644
index 0000000..84ccaec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.drawable.Drawable
+import com.android.systemui.CoreStartable
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.commandline.ParseableCommand
+import com.android.systemui.statusbar.commandline.Type
+import com.android.systemui.util.time.SystemClock
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on
+ * adb commands sent by the user.
+ *
+ * Example adb commands:
+ *
+ * To show a chip with the SysUI icon and custom text:
+ * ```
+ * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min
+ * ```
+ *
+ * To hide the chip:
+ * ```
+ * adb shell cmd statusbar demo-ron --hide
+ * ```
+ *
+ * See [DemoRonCommand] for more information on the adb command spec.
+ */
+@SysUISingleton
+class DemoRonChipViewModel
+@Inject
+constructor(
+ private val commandRegistry: CommandRegistry,
+ private val packageManager: PackageManager,
+ private val systemClock: SystemClock,
+) : OngoingActivityChipViewModel, CoreStartable {
+ override fun start() {
+ commandRegistry.registerCommand("demo-ron") { DemoRonCommand() }
+ }
+
+ private val _chip =
+ MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
+ override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
+
+ private inner class DemoRonCommand : ParseableCommand("demo-ron") {
+ private val packageName: String? by
+ param(
+ longName = "packageName",
+ shortName = "p",
+ description = "The package name for the demo RON app",
+ valueParser = Type.String,
+ )
+
+ private val text: String? by
+ param(
+ longName = "text",
+ shortName = "t",
+ description = "Text to display in the chip",
+ valueParser = Type.String,
+ )
+
+ private val hide by
+ flag(
+ longName = "hide",
+ description = "Hides any existing demo RON chip",
+ )
+
+ override fun execute(pw: PrintWriter) {
+ if (!StatusBarRonChips.isEnabled) {
+ pw.println(
+ "Error: com.android.systemui.status_bar_ron_chips must be enabled " +
+ "before using this demo feature"
+ )
+ return
+ }
+
+ if (hide) {
+ _chip.value = OngoingActivityChipModel.Hidden()
+ return
+ }
+
+ val currentPackageName = packageName
+ if (currentPackageName == null) {
+ pw.println("--packageName (or -p) must be included")
+ return
+ }
+
+ val appIcon = getAppIcon(currentPackageName)
+ if (appIcon == null) {
+ pw.println("Package $currentPackageName could not be found")
+ return
+ }
+
+ val currentText = text
+ if (currentText != null) {
+ _chip.value =
+ OngoingActivityChipModel.Shown.Text(
+ icon = appIcon,
+ // TODO(b/361346412): Include a demo with a custom color theme.
+ colors = ColorsModel.Themed,
+ text = currentText,
+ )
+ } else {
+ _chip.value =
+ OngoingActivityChipModel.Shown.Timer(
+ icon = appIcon,
+ // TODO(b/361346412): Include a demo with a custom color theme.
+ colors = ColorsModel.Themed,
+ startTimeMs = systemClock.elapsedRealtime(),
+ onClickListener = null,
+ )
+ }
+ }
+
+ private fun getAppIcon(packageName: String): OngoingActivityChipModel.ChipIcon? {
+ lateinit var iconDrawable: Drawable
+ try {
+ // Note: For the real implementation, we should check if applicationInfo exists
+ // before fetching the icon, so that we either don't show the chip or show a good
+ // backup icon in case the app info can't be found for some reason.
+ iconDrawable = packageManager.getApplicationIcon(packageName)
+ } catch (e: NameNotFoundException) {
+ return null
+ }
+ return OngoingActivityChipModel.ChipIcon.FullColorAppIcon(
+ Icon.Loaded(drawable = iconDrawable, contentDescription = null),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
similarity index 72%
copy from packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
index 62641fe..4ef1909 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.shared
+package com.android.systemui.statusbar.chips.ron.shared
import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
-/** Helper for reading or using the notifications heads up refactor flag state. */
+/** Helper for reading or using the status bar RON chips flag state. */
@Suppress("NOTHING_TO_INLINE")
-object NotificationsHeadsUpRefactor {
+object StatusBarRonChips {
/** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
/** A token used for dependency declaration */
val token: FlagToken
@@ -33,7 +33,7 @@
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.notificationsHeadsUpRefactor()
+ get() = Flags.statusBarRonChips()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
@@ -46,6 +46,14 @@
/**
* Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
* the flag is enabled to ensure that the refactor author catches issues in testing.
*/
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index 9e6cacb..eb73521 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -80,7 +80,7 @@
is ScreenRecordChipModel.Recording -> {
OngoingActivityChipModel.Shown.Timer(
icon =
- OngoingActivityChipModel.ChipIcon.Basic(
+ OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
ICON,
ContentDescription.Resource(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 7897f93..d99a916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -110,7 +110,7 @@
): OngoingActivityChipModel.Shown {
return OngoingActivityChipModel.Shown.Timer(
icon =
- OngoingActivityChipModel.ChipIcon.Basic(
+ OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
SHARE_TO_APP_ICON,
ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 26a2f91..62622a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -89,6 +89,16 @@
) : Shown(icon = null, colors, onClickListener = null) {
override val logName = "Shown.Countdown"
}
+
+ /** This chip shows the specified [text] in the chip. */
+ data class Text(
+ override val icon: ChipIcon,
+ override val colors: ColorsModel,
+ // TODO(b/361346412): Enforce a max length requirement?
+ val text: String,
+ ) : Shown(icon, colors, onClickListener = null) {
+ override val logName = "Shown.Text"
+ }
}
/** Represents an icon to show on the chip. */
@@ -106,7 +116,13 @@
}
}
- /** The icon is a basic resource or drawable icon that System UI created internally. */
- data class Basic(val impl: Icon) : ChipIcon
+ /**
+ * This icon is a single color and it came from basic resource or drawable icon that System
+ * UI created internally.
+ */
+ data class SingleColorIcon(val impl: Icon) : ChipIcon
+
+ /** This icon is an app icon in full color (so it should not get tinted in any way). */
+ data class FullColorAppIcon(val impl: Icon) : ChipIcon
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index b0d897d..04c4516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -23,6 +23,8 @@
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
@@ -51,6 +53,7 @@
shareToAppChipViewModel: ShareToAppChipViewModel,
castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
callChipViewModel: CallChipViewModel,
+ demoRonChipViewModel: DemoRonChipViewModel,
@StatusBarChipsLog private val logger: LogBuffer,
) {
private enum class ChipType {
@@ -58,6 +61,8 @@
ShareToApp,
CastToOtherDevice,
Call,
+ /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */
+ DemoRon,
}
/** Model that helps us internally track the various chip states from each of the types. */
@@ -78,6 +83,7 @@
val shareToApp: OngoingActivityChipModel.Hidden,
val castToOtherDevice: OngoingActivityChipModel.Hidden,
val call: OngoingActivityChipModel.Hidden,
+ val demoRon: OngoingActivityChipModel.Hidden,
) : InternalChipModel
}
@@ -87,7 +93,8 @@
shareToAppChipViewModel.chip,
castToOtherDeviceChipViewModel.chip,
callChipViewModel.chip,
- ) { screenRecord, shareToApp, castToOtherDevice, call ->
+ demoRonChipViewModel.chip,
+ ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
logger.log(
TAG,
LogLevel.INFO,
@@ -98,7 +105,15 @@
},
{ "Chips: ScreenRecord=$str1 > ShareToApp=$str2 > CastToOther=$str3..." },
)
- logger.log(TAG, LogLevel.INFO, { str1 = call.logName }, { "... > Call=$str1" })
+ logger.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = call.logName
+ str2 = demoRon.logName
+ },
+ { "... > Call=$str1 > DemoRon=$str2" }
+ )
// This `when` statement shows the priority order of the chips.
when {
// Screen recording also activates the media projection APIs, so whenever the
@@ -113,17 +128,23 @@
InternalChipModel.Shown(ChipType.CastToOtherDevice, castToOtherDevice)
call is OngoingActivityChipModel.Shown ->
InternalChipModel.Shown(ChipType.Call, call)
+ demoRon is OngoingActivityChipModel.Shown -> {
+ StatusBarRonChips.assertInNewMode()
+ InternalChipModel.Shown(ChipType.DemoRon, demoRon)
+ }
else -> {
// We should only get here if all chip types are hidden
check(screenRecord is OngoingActivityChipModel.Hidden)
check(shareToApp is OngoingActivityChipModel.Hidden)
check(castToOtherDevice is OngoingActivityChipModel.Hidden)
check(call is OngoingActivityChipModel.Hidden)
+ check(demoRon is OngoingActivityChipModel.Hidden)
InternalChipModel.Hidden(
screenRecord = screenRecord,
shareToApp = shareToApp,
castToOtherDevice = castToOtherDevice,
call = call,
+ demoRon = demoRon,
)
}
}
@@ -154,6 +175,7 @@
ChipType.ShareToApp -> new.shareToApp
ChipType.CastToOtherDevice -> new.castToOtherDevice
ChipType.Call -> new.call
+ ChipType.DemoRon -> new.demoRon
}
} else if (new is InternalChipModel.Shown) {
// If we have a chip to show, always show it.
@@ -179,6 +201,7 @@
shareToApp = OngoingActivityChipModel.Hidden(),
castToOtherDevice = OngoingActivityChipModel.Hidden(),
call = OngoingActivityChipModel.Hidden(),
+ demoRon = OngoingActivityChipModel.Hidden(),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index ecb6d7f..406a664 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -22,6 +22,7 @@
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.data.StatusBarDataLayerModule
import com.android.systemui.statusbar.phone.LightBarController
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
@@ -51,6 +52,11 @@
@ClassKey(LightBarController::class)
abstract fun bindLightBarController(impl: LightBarController): CoreStartable
+ @Binds
+ @IntoMap
+ @ClassKey(StatusBarSignalPolicy::class)
+ abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
+
companion object {
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index 74ec7ed..aa203d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -21,11 +21,11 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -58,7 +58,7 @@
/** Set of currently pinned top-level heads up rows to be displayed. */
val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
headsUpRepository.activeHeadsUpRows.flatMapLatest { repositories ->
@@ -80,7 +80,7 @@
/** Are there any pinned heads up rows to display? */
val hasPinnedRows: Flow<Boolean> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
headsUpRepository.activeHeadsUpRows.flatMapLatest { rows ->
@@ -95,7 +95,7 @@
}
val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
combine(hasPinnedRows, headsUpRepository.isHeadsUpAnimatingAway) {
@@ -123,7 +123,7 @@
}
val showHeadsUpStatusBar: Flow<Boolean> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 77c26cb..1f767aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -114,7 +114,6 @@
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
@@ -4304,7 +4303,7 @@
// Resetting headsUpAnimatingAway on Shade expansion avoids delays caused by
// waiting for all child animations to finish.
// TODO(b/328390331) Do we need to reset this on QS expanded as well?
- if (NotificationsHeadsUpRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
setHeadsUpAnimatingAway(false);
}
} else {
@@ -4415,7 +4414,7 @@
void onChildAnimationFinished() {
setAnimationRunning(false);
- if (NotificationsHeadsUpRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
setHeadsUpAnimatingAway(false);
}
requestChildrenUpdate();
@@ -4961,7 +4960,7 @@
}
public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
+ SceneContainerFlag.assertInLegacyMode();
ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
generateHeadsUpAnimation(row, isHeadsUp);
}
@@ -5004,7 +5003,7 @@
mNeedsAnimation = true;
if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
row.setHeadsUpAnimatingAway(true);
- if (NotificationsHeadsUpRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
setHeadsUpAnimatingAway(true);
}
}
@@ -5198,7 +5197,7 @@
updateClipping();
}
- /** TODO(b/328390331) make this private, when {@link NotificationsHeadsUpRefactor} is removed */
+ /** TODO(b/328390331) make this private, when {@link SceneContainerFlag} is removed */
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
if (mHeadsUpAnimatingAway != headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 4e73529..08d3e9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -127,7 +127,6 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpNotificationViewControllerEmptyImpl;
@@ -686,13 +685,13 @@
new OnHeadsUpChangedListener() {
@Override
public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
+ SceneContainerFlag.assertInLegacyMode();
mView.setInHeadsUpPinnedMode(inPinnedMode);
}
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
+ SceneContainerFlag.assertInLegacyMode();
NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
mView.setTopHeadsUpRow(topEntry != null ? topEntry.getRow() : null);
generateHeadsUpAnimation(entry, isHeadsUp);
@@ -880,7 +879,7 @@
});
}
- if (!NotificationsHeadsUpRefactor.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
}
mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
@@ -1508,7 +1507,7 @@
}
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
- NotificationsHeadsUpRefactor.assertInLegacyMode();
+ SceneContainerFlag.assertInLegacyMode();
mView.setHeadsUpAnimatingAway(headsUpAnimatingAway);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 5572f8e..d770b20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -27,6 +27,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
@@ -36,7 +37,6 @@
import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
import com.android.systemui.statusbar.notification.stack.DisplaySwitchNotificationsHiderTracker
@@ -93,7 +93,7 @@
view.repeatWhenAttached {
lifecycleScope.launch {
- if (NotificationsHeadsUpRefactor.isEnabled) {
+ if (SceneContainerFlag.isEnabled) {
launch { hunBinder.bindHeadsUpNotifications(view) }
}
launch { bindShelf(shelf) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 5fba615..e55492e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -26,7 +27,6 @@
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
@@ -256,7 +256,7 @@
}
val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(null)
} else {
headsUpNotificationInteractor.topHeadsUpRow.dumpWhileCollecting("topHeadsUpRow")
@@ -264,7 +264,7 @@
}
val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(emptySet())
} else {
headsUpNotificationInteractor.pinnedHeadsUpRows.dumpWhileCollecting("pinnedHeadsUpRows")
@@ -272,7 +272,7 @@
}
val headsUpAnimationsEnabled: Flow<Boolean> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
flowOf(true).dumpWhileCollecting("headsUpAnimationsEnabled")
@@ -280,7 +280,7 @@
}
val hasPinnedHeadsUpRow: Flow<Boolean> by lazy {
- if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
headsUpNotificationInteractor.hasPinnedRows.dumpWhileCollecting("hasPinnedHeadsUpRow")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index e3242d1..7227b93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,6 +31,7 @@
import static com.android.systemui.Flags.lightRevealMigration;
import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
+import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -870,7 +871,10 @@
mBubblesOptional.ifPresent(this::initBubbles);
mKeyguardBypassController.listenForQsExpandedChange();
- mStatusBarSignalPolicy.init();
+ if (!statusBarSignalPolicyRefactor()) {
+ mStatusBarSignalPolicy.init();
+ }
+
mKeyguardIndicationController.init();
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 0ea28a7..1efad3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -47,7 +47,6 @@
import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.AnimationStateHandler;
import com.android.systemui.statusbar.policy.AvalancheController;
@@ -284,7 +283,7 @@
private void onShadeOrQsExpanded(Boolean isExpanded) {
if (isExpanded != mIsExpanded) {
mIsExpanded = isExpanded;
- if (!NotificationsHeadsUpRefactor.isEnabled() && isExpanded) {
+ if (!SceneContainerFlag.isEnabled() && isExpanded) {
mHeadsUpAnimatingAway.setValue(false);
}
}
@@ -517,7 +516,7 @@
@Nullable
private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
- if (NotificationsHeadsUpRefactor.isEnabled()) {
+ if (SceneContainerFlag.isEnabled()) {
return (HeadsUpEntryPhone) mTopHeadsUpRow.getValue();
} else {
return (HeadsUpEntryPhone) getTopHeadsUpEntry();
@@ -711,7 +710,7 @@
}
private NotificationEntry requireEntry() {
- /* check if */ NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode();
+ /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode();
return Objects.requireNonNull(mEntry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index da5877b..8f2d4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -19,12 +19,12 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -112,7 +112,7 @@
}
private void setHeadsAnimatingAway(boolean headsUpAnimatingAway) {
- if (!NotificationsHeadsUpRefactor.isEnabled()) {
+ if (!SceneContainerFlag.isEnabled()) {
mHeadsUpManager.setHeadsUpAnimatingAway(headsUpAnimatingAway);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 09b6b68..43f9af6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -68,13 +68,11 @@
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardDone;
@@ -363,8 +361,6 @@
}
}
};
- private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor;
- private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor;
private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor;
private final JavaAdapter mJavaAdapter;
private StatusBarKeyguardViewManagerInteractor mStatusBarKeyguardViewManagerInteractor;
@@ -394,11 +390,10 @@
UdfpsOverlayInteractor udfpsOverlayInteractor,
ActivityStarter activityStarter,
KeyguardTransitionInteractor keyguardTransitionInteractor,
+ KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor,
@Main CoroutineDispatcher mainDispatcher,
- Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
SelectedUserInteractor selectedUserInteractor,
- Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
JavaAdapter javaAdapter,
Lazy<SceneInteractor> sceneInteractorLazy,
StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor,
@@ -432,11 +427,10 @@
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
mActivityStarter = activityStarter;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mKeyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor;
mMainDispatcher = mainDispatcher;
- mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor;
mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
mSelectedUserInteractor = selectedUserInteractor;
- mSurfaceBehindInteractor = surfaceBehindInteractor;
mJavaAdapter = javaAdapter;
mSceneInteractorLazy = sceneInteractorLazy;
mStatusBarKeyguardViewManagerInteractor = statusBarKeyguardViewManagerInteractor;
@@ -445,6 +439,7 @@
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor;
CoroutineDispatcher mMainDispatcher;
@Override
@@ -1616,7 +1611,7 @@
}
if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+ mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition(
"SBKVM#keyguardAuthenticated");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ba59398..d5fafe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
+
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
@@ -23,16 +25,19 @@
import android.util.Log;
import com.android.settingslib.mobile.TelephonyIcons;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.CarrierConfigTracker;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.util.ArrayList;
import java.util.List;
@@ -40,10 +45,13 @@
import javax.inject.Inject;
-/** Controls the signal policies for icons shown in the statusbar. **/
+/** Controls the signal policies for icons shown in the statusbar. */
@SysUISingleton
-public class StatusBarSignalPolicy implements SignalCallback,
- SecurityController.SecurityControllerCallback, Tunable {
+public class StatusBarSignalPolicy
+ implements SignalCallback,
+ SecurityController.SecurityControllerCallback,
+ Tunable,
+ CoreStartable {
private static final String TAG = "StatusBarSignalPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -61,16 +69,15 @@
private final Handler mHandler = Handler.getMain();
private final CarrierConfigTracker mCarrierConfigTracker;
private final TunerService mTunerService;
+ private final JavaAdapter mJavaAdapter;
+ private final AirplaneModeInteractor mAirplaneModeInteractor;
private boolean mHideAirplane;
private boolean mHideMobile;
private boolean mHideEthernet;
- private boolean mActivityEnabled;
+ private final boolean mActivityEnabled;
- // Track as little state as possible, and only for padding purposes
- private boolean mIsAirplaneMode = false;
-
- private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
+ private final ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
private boolean mInitialized;
@Inject
@@ -80,15 +87,19 @@
CarrierConfigTracker carrierConfigTracker,
NetworkController networkController,
SecurityController securityController,
- TunerService tunerService
+ TunerService tunerService,
+ JavaAdapter javaAdapter,
+ AirplaneModeInteractor airplaneModeInteractor
) {
mContext = context;
mIconController = iconController;
mCarrierConfigTracker = carrierConfigTracker;
+ mJavaAdapter = javaAdapter;
mNetworkController = networkController;
mSecurityController = securityController;
mTunerService = tunerService;
+ mAirplaneModeInteractor = airplaneModeInteractor;
mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -100,15 +111,35 @@
mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
}
+ @Override
+ public void start() {
+ if (!statusBarSignalPolicyRefactor()) {
+ return;
+ }
+
+ mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
+ mNetworkController.addCallback(this);
+ mSecurityController.addCallback(this);
+
+ mJavaAdapter.alwaysCollectFlow(
+ mAirplaneModeInteractor.isAirplaneMode(), this::updateAirplaneModeIcon);
+ }
+
/** Call to initialize and register this class with the system. */
public void init() {
- if (mInitialized) {
+ if (mInitialized || statusBarSignalPolicyRefactor()) {
return;
}
mInitialized = true;
mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
+
+ if (statusBarSignalPolicyRefactor()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mAirplaneModeInteractor.isAirplaneMode(),
+ this::updateAirplaneModeIcon);
+ }
}
public void destroy() {
@@ -222,15 +253,19 @@
@Override
public void setIsAirplaneMode(IconState icon) {
+ if (statusBarSignalPolicyRefactor()) {
+ return;
+ }
+
if (DEBUG) {
Log.d(TAG, "setIsAirplaneMode: "
+ "icon = " + (icon == null ? "" : icon.toString()));
}
- mIsAirplaneMode = icon.visible && !mHideAirplane;
+ boolean isAirplaneMode = icon.visible && !mHideAirplane;
int resId = icon.icon;
String description = icon.contentDescription;
- if (mIsAirplaneMode && resId > 0) {
+ if (isAirplaneMode && resId > 0) {
mIconController.setIcon(mSlotAirplane, resId, description);
mIconController.setIconVisibility(mSlotAirplane, true);
} else {
@@ -238,6 +273,21 @@
}
}
+ public void updateAirplaneModeIcon(boolean isAirplaneModeOn) {
+ if (StatusBarSignalPolicyRefactor.isUnexpectedlyInLegacyMode()) {
+ return;
+ }
+
+ boolean isAirplaneMode = isAirplaneModeOn && !mHideAirplane;
+ mIconController.setIconVisibility(mSlotAirplane, isAirplaneMode);
+ if (isAirplaneMode) {
+ mIconController.setIcon(
+ mSlotAirplane,
+ TelephonyIcons.FLIGHT_MODE_ICON,
+ mContext.getString(R.string.accessibility_airplane_mode));
+ }
+ }
+
/**
* Stores the statusbar state for no Calling & SMS.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt
index 62641fe..0577f49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.shared
+package com.android.systemui.statusbar.phone
import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
-/** Helper for reading or using the notifications heads up refactor flag state. */
+/** Helper for reading or using the status_bar_signal_policy_refactor flag state. */
@Suppress("NOTHING_TO_INLINE")
-object NotificationsHeadsUpRefactor {
+object StatusBarSignalPolicyRefactor {
/** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR
/** A token used for dependency declaration */
val token: FlagToken
@@ -33,7 +33,7 @@
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.notificationsHeadsUpRefactor()
+ get() = Flags.statusBarSignalPolicyRefactor()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
index 1a55f7d..f5cfc8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepository.kt
@@ -31,7 +31,7 @@
import androidx.annotation.ArrayRes
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
import com.android.systemui.tuner.TunerService
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -252,7 +253,10 @@
}
// Only CELLULAR networks may have underlying wifi information that's relevant to SysUI,
// so skip the underlying network check if it's not CELLULAR.
- if (!this.hasTransport(TRANSPORT_CELLULAR)) {
+ if (
+ !this.hasTransport(TRANSPORT_CELLULAR) &&
+ !Flags.statusBarAlwaysCheckUnderlyingNetworks()
+ ) {
return mainWifiInfo
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index d46aaf4..c24d694 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -35,6 +35,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -124,10 +125,6 @@
// Colors
val textColor = chipModel.colors.text(chipContext)
- chipDefaultIconView.imageTintList =
- ColorStateList.valueOf(textColor)
- chipBackgroundView.getCustomIconView()?.imageTintList =
- ColorStateList.valueOf(textColor)
chipTimeView.setTextColor(textColor)
chipTextView.setTextColor(textColor)
(chipBackgroundView.background as GradientDrawable).color =
@@ -173,13 +170,22 @@
// it.
backgroundView.removeView(backgroundView.getCustomIconView())
+ val iconTint = chipModel.colors.text(defaultIconView.context)
+
when (val icon = chipModel.icon) {
null -> {
defaultIconView.visibility = View.GONE
}
- is OngoingActivityChipModel.ChipIcon.Basic -> {
+ is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> {
IconViewBinder.bind(icon.impl, defaultIconView)
defaultIconView.visibility = View.VISIBLE
+ defaultIconView.tintView(iconTint)
+ }
+ is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
+ StatusBarRonChips.assertInNewMode()
+ IconViewBinder.bind(icon.impl, defaultIconView)
+ defaultIconView.visibility = View.VISIBLE
+ defaultIconView.untintView()
}
is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
// Hide the default icon since we'll show this custom icon instead.
@@ -194,6 +200,7 @@
// maybe include the app name.
contentDescription =
context.resources.getString(R.string.ongoing_phone_call_content_description)
+ tintView(iconTint)
}
// 2. If we just reinflated the view, we may need to detach the icon view from the
@@ -219,6 +226,14 @@
return this.findViewById(CUSTOM_ICON_VIEW_ID)
}
+ private fun ImageView.tintView(color: Int) {
+ this.imageTintList = ColorStateList.valueOf(color)
+ }
+
+ private fun ImageView.untintView() {
+ this.imageTintList = null
+ }
+
private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams {
val customIconSize =
iconView.context.resources.getDimensionPixelSize(
@@ -237,10 +252,13 @@
chipTextView.text = chipModel.secondsUntilStarted.toString()
chipTextView.visibility = View.VISIBLE
- // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
- // [Chronometer.start].
- chipTimeView.stop()
- chipTimeView.visibility = View.GONE
+ chipTimeView.hide()
+ }
+ is OngoingActivityChipModel.Shown.Text -> {
+ chipTextView.text = chipModel.text
+ chipTextView.visibility = View.VISIBLE
+
+ chipTimeView.hide()
}
is OngoingActivityChipModel.Shown.Timer -> {
ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
@@ -250,14 +268,18 @@
}
is OngoingActivityChipModel.Shown.IconOnly -> {
chipTextView.visibility = View.GONE
- // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
- // [Chronometer.start].
- chipTimeView.stop()
- chipTimeView.visibility = View.GONE
+ chipTimeView.hide()
}
}
}
+ private fun ChipChronometer.hide() {
+ // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+ // [Chronometer.start].
+ this.stop()
+ this.visibility = View.GONE
+ }
+
private fun updateChipPadding(
chipModel: OngoingActivityChipModel.Shown,
backgroundView: View,
@@ -356,6 +378,7 @@
chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
}
is OngoingActivityChipModel.Shown.Timer,
+ is OngoingActivityChipModel.Shown.Text,
is OngoingActivityChipModel.Shown.IconOnly -> {
chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
index 89227cf..fe1d647 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -21,10 +21,10 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import javax.inject.Inject
@@ -58,7 +58,7 @@
) {
private val showingHeadsUpStatusBar: Flow<Boolean> =
- if (NotificationsHeadsUpRefactor.isEnabled) {
+ if (SceneContainerFlag.isEnabled) {
headsUpNotificationInteractor.showHeadsUpStatusBar
} else {
flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c7fc445..9c8ef04 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -89,6 +89,9 @@
import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.StateFlow;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -162,6 +165,7 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final JavaAdapter mJavaAdapter;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final StateFlow<Boolean> mIsKeyguardOnAsleepState;
private final UiModeManager mUiModeManager;
private ColorScheme mDarkColorScheme;
private ColorScheme mLightColorScheme;
@@ -202,8 +206,7 @@
}
boolean currentUser = userId == mUserTracker.getUserId();
boolean isAsleep = themeOverlayControllerWakefulnessDeprecation()
- ? KeyguardState.Companion.deviceIsAsleepInState(
- mKeyguardTransitionInteractor.getFinishedState())
+ ? ThemeOverlayController.this.mIsKeyguardOnAsleepState.getValue()
: mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP;
if (currentUser && !mAcceptColorEvents && isAsleep) {
@@ -434,6 +437,10 @@
mUiModeManager = uiModeManager;
mActivityManager = activityManager;
dumpManager.registerDumpable(TAG, this);
+
+ Flow<Boolean> isFinishedInAsleepStateFlow = mKeyguardTransitionInteractor
+ .isFinishedInStateWhere(KeyguardState.Companion::deviceIsAsleepInState);
+ mIsKeyguardOnAsleepState = mJavaAdapter.stateInApp(isFinishedInAsleepStateFlow, false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 055671c..64e056d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -31,7 +31,10 @@
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** A class allowing Java classes to collect on Kotlin flows. */
@@ -58,6 +61,15 @@
): Job {
return scope.launch { flow.collect { consumer.accept(it) } }
}
+
+ @JvmOverloads
+ fun <T> stateInApp(
+ flow: Flow<T>,
+ initialValue: T,
+ started: SharingStarted = SharingStarted.Eagerly
+ ): StateFlow<T> {
+ return flow.stateIn(scope, started, initialValue)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index d3e8bd3..28effe9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -125,7 +125,6 @@
static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
static {
STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
- STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
@@ -654,7 +653,6 @@
private static boolean isLogWorthy(int stream) {
switch (stream) {
case AudioSystem.STREAM_ALARM:
- case AudioSystem.STREAM_BLUETOOTH_SCO:
case AudioSystem.STREAM_MUSIC:
case AudioSystem.STREAM_RING:
case AudioSystem.STREAM_SYSTEM:
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index eb91518..7786453 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -704,8 +704,6 @@
addRow(AudioManager.STREAM_VOICE_CALL,
com.android.internal.R.drawable.ic_phone,
com.android.internal.R.drawable.ic_phone, false, false);
- addRow(AudioManager.STREAM_BLUETOOTH_SCO,
- R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false);
addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system,
R.drawable.ic_volume_system_mute, false, false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
index 0451ce6..4be680e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractor.kt
@@ -16,9 +16,7 @@
package com.android.systemui.volume.panel.component.volume.domain.interactor
-import android.media.AudioDeviceInfo
import android.media.AudioManager
-import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
@@ -42,7 +40,6 @@
constructor(
@VolumePanelScope scope: CoroutineScope,
mediaOutputInteractor: MediaOutputInteractor,
- audioRepository: AudioRepository,
audioModeInteractor: AudioModeInteractor,
) {
@@ -50,13 +47,12 @@
combineTransform(
mediaOutputInteractor.activeMediaDeviceSessions,
mediaOutputInteractor.defaultActiveMediaSession.filterData(),
- audioRepository.communicationDevice,
audioModeInteractor.isOngoingCall,
- ) { activeSessions, defaultSession, communicationDevice, isOngoingCall ->
+ ) { activeSessions, defaultSession, isOngoingCall ->
coroutineScope {
val viewModels = buildList {
if (isOngoingCall) {
- addCall(communicationDevice?.type)
+ addStream(AudioManager.STREAM_VOICE_CALL)
}
if (defaultSession?.isTheSameSession(activeSessions.remote) == true) {
@@ -68,7 +64,7 @@
}
if (!isOngoingCall) {
- addCall(communicationDevice?.type)
+ addStream(AudioManager.STREAM_VOICE_CALL)
}
addStream(AudioManager.STREAM_RING)
@@ -80,14 +76,6 @@
}
.stateIn(scope, SharingStarted.Eagerly, emptyList())
- private fun MutableList<SliderType>.addCall(communicationDeviceType: Int?) {
- if (communicationDeviceType == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
- addStream(AudioManager.STREAM_BLUETOOTH_SCO)
- } else {
- addStream(AudioManager.STREAM_VOICE_CALL)
- }
- }
-
private fun MutableList<SliderType>.addSession(remoteMediaDeviceSession: MediaDeviceSession?) {
if (remoteMediaDeviceSession?.canAdjustVolume == true) {
add(SliderType.MediaDeviceCast(remoteMediaDeviceSession))
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 521f608..ffb1f11 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -66,7 +66,6 @@
mapOf(
AudioStream(AudioManager.STREAM_MUSIC) to R.drawable.ic_music_note,
AudioStream(AudioManager.STREAM_VOICE_CALL) to R.drawable.ic_call,
- AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to R.drawable.ic_call,
AudioStream(AudioManager.STREAM_RING) to R.drawable.ic_ring_volume,
AudioStream(AudioManager.STREAM_NOTIFICATION) to R.drawable.ic_volume_ringer,
AudioStream(AudioManager.STREAM_ALARM) to R.drawable.ic_volume_alarm,
@@ -75,7 +74,6 @@
mapOf(
AudioStream(AudioManager.STREAM_MUSIC) to R.string.stream_music,
AudioStream(AudioManager.STREAM_VOICE_CALL) to R.string.stream_voice_call,
- AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to R.string.stream_voice_call,
AudioStream(AudioManager.STREAM_RING) to R.string.stream_ring,
AudioStream(AudioManager.STREAM_NOTIFICATION) to R.string.stream_notification,
AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm,
@@ -91,8 +89,6 @@
VolumePanelUiEvent.VOLUME_PANEL_MUSIC_SLIDER_TOUCHED,
AudioStream(AudioManager.STREAM_VOICE_CALL) to
VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED,
- AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to
- VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED,
AudioStream(AudioManager.STREAM_RING) to
VolumePanelUiEvent.VOLUME_PANEL_RING_SLIDER_TOUCHED,
AudioStream(AudioManager.STREAM_NOTIFICATION) to
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 4e1b12f..43c7ed6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -21,7 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -44,7 +44,8 @@
@Mock private lateinit var activityTaskManagerService: IActivityTaskManager
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier
- @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ @Mock
+ private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
@Before
fun setUp() {
@@ -57,7 +58,7 @@
activityTaskManagerService = activityTaskManagerService,
keyguardStateController = keyguardStateController,
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
- keyguardTransitionInteractor = keyguardTransitionInteractor,
+ keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 73b9f57..07f7557 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -47,8 +47,11 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
@@ -56,7 +59,10 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -144,7 +150,6 @@
@Mock
private lateinit var glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel
- @Mock private lateinit var transitionInteractor: KeyguardTransitionInteractor
private val kosmos = testKosmos()
@@ -163,8 +168,6 @@
// the viewModel does a `map { 1 - it }` on this value, which is why it's different
private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f)
- private val intendedFinishedKeyguardStateFlow = MutableStateFlow(KeyguardState.LOCKSCREEN)
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -256,7 +259,6 @@
intendedAlphaMutableStateFlow.value = 1f
intendedShadeAlphaMutableStateFlow.value = 0f
- intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
.thenReturn(intendedAlphaMutableStateFlow)
whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
@@ -283,8 +285,6 @@
whenever(glanceableHubToLockscreenTransitionViewModel.shortcutsAlpha)
.thenReturn(emptyFlow())
whenever(shadeInteractor.anyExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
- whenever(transitionInteractor.finishedKeyguardState)
- .thenReturn(intendedFinishedKeyguardStateFlow)
underTest =
KeyguardQuickAffordancesCombinedViewModel(
@@ -334,7 +334,7 @@
lockscreenToPrimaryBouncerTransitionViewModel,
lockscreenToGlanceableHubTransitionViewModel =
lockscreenToGlanceableHubTransitionViewModel,
- transitionInteractor = transitionInteractor,
+ transitionInteractor = kosmos.keyguardTransitionInteractor,
)
}
@@ -776,7 +776,10 @@
@Test
fun shadeExpansionAlpha_changes_whenOnLockscreen() =
testScope.runTest {
- intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Lockscreen),
+ stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN)
+ )
intendedShadeAlphaMutableStateFlow.value = 0.25f
val underTest = collectLastValue(underTest.transitionAlpha)
assertEquals(0.75f, underTest())
@@ -788,7 +791,10 @@
@Test
fun shadeExpansionAlpha_alwaysZero_whenNotOnLockscreen() =
testScope.runTest {
- intendedFinishedKeyguardStateFlow.value = KeyguardState.GONE
+ kosmos.setTransition(
+ sceneTransition = Idle(Scenes.Gone),
+ stateTransition = TransitionStep(from = AOD, to = GONE)
+ )
intendedShadeAlphaMutableStateFlow.value = 0.5f
val underTest = collectLastValue(underTest.transitionAlpha)
assertEquals(0f, underTest())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 5a5cdcd..c8ff52a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -417,13 +417,17 @@
// Communal is open.
goToScene(CommunalScenes.Communal)
- // Shade shows up.
- shadeTestUtil.setQsExpansion(0.5f)
- testableLooper.processAllMessages()
+ // Touch starts and ends.
assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
assertThat(underTest.onTouchEvent(CANCEL_EVENT)).isTrue()
+
+ // Up event is no longer processed
assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+
+ // Move event can still be processed
assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+ assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
}
}
@@ -716,6 +720,30 @@
}
@Test
+ fun onTouchEvent_shadeExpanding_touchesNotDispatched() =
+ with(kosmos) {
+ testScope.runTest {
+ // On lockscreen.
+ goToScene(CommunalScenes.Blank)
+ whenever(
+ notificationStackScrollLayoutController.isBelowLastNotification(
+ any(),
+ any()
+ )
+ )
+ .thenReturn(true)
+
+ // Shade is open slightly.
+ fakeShadeRepository.setLegacyShadeExpansion(0.01f)
+ testableLooper.processAllMessages()
+
+ // Touches are not consumed.
+ assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+ verify(containerView, never()).onTouchEvent(DOWN_EVENT)
+ }
+ }
+
+ @Test
fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 523d15c..9481e5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -155,7 +155,6 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository;
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor;
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -163,7 +162,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
-import com.android.systemui.statusbar.notification.stack.data.repository.FakeHeadsUpNotificationRepository;
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -365,13 +363,6 @@
protected TestScope mTestScope = mKosmos.getTestScope();
protected ShadeInteractor mShadeInteractor;
protected PowerInteractor mPowerInteractor;
- protected FakeHeadsUpNotificationRepository mFakeHeadsUpNotificationRepository =
- new FakeHeadsUpNotificationRepository();
- protected NotificationsKeyguardViewStateRepository mNotificationsKeyguardViewStateRepository =
- new NotificationsKeyguardViewStateRepository();
- protected NotificationsKeyguardInteractor mNotificationsKeyguardInteractor =
- new NotificationsKeyguardInteractor(mNotificationsKeyguardViewStateRepository);
- protected HeadsUpNotificationInteractor mHeadsUpNotificationInteractor;
protected NotificationPanelViewController.TouchHandler mTouchHandler;
protected ConfigurationController mConfigurationController;
protected SysuiStatusBarStateController mStatusBarStateController;
@@ -689,12 +680,6 @@
when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes);
when(longPressHandlingViewRes.getString(anyInt())).thenReturn("");
-
- mHeadsUpNotificationInteractor =
- new HeadsUpNotificationInteractor(mFakeHeadsUpNotificationRepository,
- mDeviceEntryFaceAuthInteractor, mKeyguardTransitionInteractor,
- mNotificationsKeyguardInteractor, mShadeInteractor);
-
mNotificationPanelViewController = new NotificationPanelViewController(
mView,
mMainHandler,
@@ -769,7 +754,6 @@
mActivityStarter,
mSharedNotificationContainerInteractor,
mActiveNotificationsInteractor,
- mHeadsUpNotificationInteractor,
mShadeAnimationInteractor,
mKeyguardViewConfigurator,
mDeviceEntryFaceAuthInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 905cc4c..a7fd160 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -58,13 +58,13 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.DejankUtils;
+import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
@@ -1375,7 +1375,7 @@
}
@Test
- @DisableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @DisableSceneContainer
public void shadeExpanded_whenHunIsPresent() {
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index 64eadb7..90655c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -19,7 +19,6 @@
package com.android.systemui.shade
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import android.view.HapticFeedbackConstants
import android.view.View
@@ -35,7 +34,6 @@
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -243,36 +241,4 @@
val bottomAreaAlpha by collectLastValue(mFakeKeyguardRepository.bottomAreaAlpha)
assertThat(bottomAreaAlpha).isEqualTo(1f)
}
-
- @Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
- fun shadeExpanded_whenHunIsPresent() = runTest {
- launch(mainDispatcher) {
- givenViewAttached()
-
- // WHEN a pinned heads up is present
- mFakeHeadsUpNotificationRepository.setNotifications(
- FakeHeadsUpRowRepository("key", isPinned = true)
- )
- }
- advanceUntilIdle()
-
- // THEN the panel should be visible
- assertThat(mNotificationPanelViewController.isExpanded).isTrue()
- }
-
- @Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
- fun shadeExpanded_whenHunIsAnimatingAway() = runTest {
- launch(mainDispatcher) {
- givenViewAttached()
-
- // WHEN a heads up is animating away
- mFakeHeadsUpNotificationRepository.isHeadsUpAnimatingAway.value = true
- }
- advanceUntilIdle()
-
- // THEN the panel should be visible
- assertThat(mNotificationPanelViewController.isExpanded).isTrue()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
new file mode 100644
index 0000000..593f873
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.statusbar.connectivity.IconState
+import com.android.systemui.statusbar.connectivity.NetworkController
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy_Factory
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
+import com.android.systemui.statusbar.policy.SecurityController
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.verifyZeroInteractions
+import kotlin.test.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarSignalPolicyTest : SysuiTestCase() {
+ private val kosmos = Kosmos().also { it.testCase = this }
+
+ private lateinit var underTest: StatusBarSignalPolicy
+
+ private val testScope = TestScope()
+
+ private val javaAdapter = JavaAdapter(testScope.backgroundScope)
+ private val airplaneModeInteractor = kosmos.airplaneModeInteractor
+
+ private val securityController = mock<SecurityController>()
+ private val tunerService = mock<TunerService>()
+ private val statusBarIconController = mock<StatusBarIconController>()
+ private val networkController = mock<NetworkController>()
+ private val carrierConfigTracker = mock<CarrierConfigTracker>()
+
+ private var slotAirplane: String? = null
+
+ @Before
+ fun setup() {
+ underTest =
+ StatusBarSignalPolicy_Factory.newInstance(
+ mContext,
+ statusBarIconController,
+ carrierConfigTracker,
+ networkController,
+ securityController,
+ tunerService,
+ javaAdapter,
+ airplaneModeInteractor,
+ )
+
+ slotAirplane = mContext.getString(R.string.status_bar_airplane)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+ fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagEnabled_iconUpdated() =
+ testScope.runTest {
+ underTest.start()
+ airplaneModeInteractor.setIsAirplaneMode(true)
+ runCurrent()
+ verify(statusBarIconController).setIconVisibility(slotAirplane, true)
+
+ airplaneModeInteractor.setIsAirplaneMode(false)
+ runCurrent()
+ verify(statusBarIconController).setIconVisibility(slotAirplane, false)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+ fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagEnabled_iconNotUpdated() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+ clearInvocations(statusBarIconController)
+
+ // Make sure the legacy code path does not change airplane mode when the refactor
+ // flag is enabled.
+ underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ runCurrent()
+ verifyZeroInteractions(statusBarIconController)
+
+ underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ runCurrent()
+ verifyZeroInteractions(statusBarIconController)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+ fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagEnabled_initNoOp() =
+ testScope.runTest {
+ // Make sure StatusBarSignalPolicy.init does no initialization when
+ // the refactor flag is disabled.
+ underTest.init()
+ verifyZeroInteractions(securityController, networkController, tunerService)
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+ fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagDisabled_iconUpdated() =
+ testScope.runTest {
+ underTest.init()
+
+ underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ runCurrent()
+ verify(statusBarIconController).setIconVisibility(slotAirplane, true)
+
+ underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+ runCurrent()
+ verify(statusBarIconController).setIconVisibility(slotAirplane, false)
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+ fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagDisabled_iconNotUpdated() =
+ testScope.runTest {
+ underTest.init()
+
+ // Make sure changing airplane mode from airplaneModeRepository does nothing
+ // if the StatusBarSignalPolicyRefactor is not enabled.
+ airplaneModeInteractor.setIsAirplaneMode(true)
+ runCurrent()
+ verifyZeroInteractions(statusBarIconController)
+
+ airplaneModeInteractor.setIsAirplaneMode(false)
+ runCurrent()
+ verifyZeroInteractions(statusBarIconController)
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+ fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagDisabled_startNoOp() =
+ testScope.runTest {
+ // Make sure StatusBarSignalPolicy.start does no initialization when
+ // the refactor flag is disabled.
+ underTest.start()
+ verifyZeroInteractions(securityController, networkController, tunerService)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index ce79fbd..7bc6d4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -132,10 +132,10 @@
)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+ .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
assertThat(icon.contentDescription).isNotNull()
@@ -170,10 +170,10 @@
)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+ .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
assertThat(icon.contentDescription).isNotNull()
@@ -206,10 +206,10 @@
repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null))
assertThat((latest as OngoingActivityChipModel.Shown).icon)
- .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+ .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
assertThat(icon.contentDescription).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index a8d2c5b..77992db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -127,7 +127,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
assertThat((icon.contentDescription as ContentDescription.Resource).res)
@@ -146,7 +146,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
assertThat((icon.contentDescription as ContentDescription.Resource).res)
@@ -184,7 +184,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
// This content description is just generic "Casting", not "Casting screen"
@@ -214,7 +214,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
// MediaProjection == screen casting, so this content description reflects that we're
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
new file mode 100644
index 0000000..8576893
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+
+import android.content.packageManager
+import android.graphics.drawable.BitmapDrawable
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class DemoRonChipViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val commandRegistry = kosmos.commandRegistry
+ private val pw = PrintWriter(StringWriter())
+
+ private val underTest = kosmos.demoRonChipViewModel
+
+ @Before
+ fun setUp() {
+ underTest.start()
+ whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
+ .thenReturn(BitmapDrawable())
+ }
+
+ @Test
+ @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ fun chip_flagOff_hidden() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ addDemoRonChip()
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ fun chip_noPackage_hidden() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron"))
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ fun chip_hasPackage_shown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ fun chip_hasText_shownWithText() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ commandRegistry.onShellCommand(
+ pw,
+ arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test")
+ )
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
+ }
+
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ fun chip_hasHideArg_hidden() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ // First, show a chip
+ addDemoRonChip()
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+
+ // Then, hide the chip
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide"))
+
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ private fun addDemoRonChip() {
+ Companion.addDemoRonChip(commandRegistry, pw)
+ }
+
+ companion object {
+ fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
+ commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 804eb5c..16101bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -150,7 +150,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord)
assertThat(icon.contentDescription).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index a2ef599..791a21d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -135,7 +135,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
assertThat(icon.contentDescription).isNotNull()
@@ -152,7 +152,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
assertThat(icon.contentDescription).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index a724cfaa..4977c548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -154,5 +154,7 @@
}
private fun createIcon(@DrawableRes drawable: Int) =
- OngoingActivityChipModel.ChipIcon.Basic(Icon.Resource(drawable, contentDescription = null))
+ OngoingActivityChipModel.ChipIcon.SingleColorIcon(
+ Icon.Resource(drawable, contentDescription = null)
+ )
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 556ec6a..bd5df07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -19,9 +19,12 @@
import android.content.DialogInterface
import android.content.packageManager
import android.content.pm.PackageManager
+import android.graphics.drawable.BitmapDrawable
+import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -36,8 +39,11 @@
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -45,6 +51,8 @@
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -68,11 +76,14 @@
private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
private val systemClock = kosmos.fakeSystemClock
+ private val commandRegistry = kosmos.commandRegistry
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
private val callRepo = kosmos.ongoingCallRepository
+ private val pw = PrintWriter(StringWriter())
+
private val mockSystemUIDialog = mock<SystemUIDialog>()
private val chipBackgroundView = mock<ChipBackgroundContainer>()
private val chipView =
@@ -90,6 +101,9 @@
@Before
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
+ kosmos.demoRonChipViewModel.start()
+ whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
+ .thenReturn(BitmapDrawable())
}
@Test
@@ -169,15 +183,24 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
testScope.runTest {
- // Start with just the lower priority call chip
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ // Start with just the lowest priority chip shown
+ addDemoRonChip(commandRegistry, pw)
+ // And everything else hidden
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
mediaProjectionState.value = MediaProjectionState.NotProjecting
screenRecordState.value = ScreenRecordModel.DoingNothing
val latest by collectLastValue(underTest.chip)
+ assertIsDemoRonChip(latest)
+
+ // WHEN the higher priority call chip is added
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+ // THEN the higher priority call chip is used
assertIsCallChip(latest)
// WHEN the higher priority media projection chip is added
@@ -199,14 +222,15 @@
}
@Test
+ @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
fun chip_highestPriorityChipRemoved_showsNextPriorityChip() =
testScope.runTest {
// WHEN all chips are active
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addDemoRonChip(commandRegistry, pw)
val latest by collectLastValue(underTest.chip)
@@ -224,6 +248,12 @@
// THEN the lower priority call is used
assertIsCallChip(latest)
+
+ // WHEN the higher priority call is removed
+ callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+ // THEN the lower priority demo RON is used
+ assertIsDemoRonChip(latest)
}
/** Regression test for b/347726238. */
@@ -338,7 +368,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord)
}
@@ -347,7 +377,7 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
}
@@ -356,9 +386,15 @@
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
val icon =
(((latest as OngoingActivityChipModel.Shown).icon)
- as OngoingActivityChipModel.ChipIcon.Basic)
+ as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
.impl as Icon.Resource
assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
}
+
+ fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 1717f4c..8d1228c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -95,7 +95,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -1206,7 +1205,7 @@
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
public void testGenerateHeadsUpDisappearEvent_setsHeadsUpAnimatingAway() {
// GIVEN NSSL is ready for HUN animations
Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1222,7 +1221,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
public void testGenerateHeadsUpDisappearEvent_stackExpanded_headsUpAnimatingAwayNotSet() {
// GIVEN NSSL would be ready for HUN animations, BUT it is expanded
Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1241,7 +1240,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
public void testGenerateHeadsUpDisappearEvent_pendingAppearEvent_headsUpAnimatingAwayNotSet() {
// GIVEN NSSL is ready for HUN animations
Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1259,7 +1258,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
public void testGenerateHeadsUpAppearEvent_headsUpAnimatingAwayNotSet() {
// GIVEN NSSL is ready for HUN animations
Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1295,7 +1294,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
public void testOnChildAnimationsFinished_resetsheadsUpAnimatingAway() {
// GIVEN NSSL is ready for HUN animations
Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 54c03e8..3e3c046 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -84,10 +84,9 @@
import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -235,11 +234,10 @@
mUdfpsOverlayInteractor,
mActivityStarter,
mKeyguardTransitionInteractor,
+ mock(KeyguardDismissTransitionInteractor.class),
StandardTestDispatcher(null, null),
- () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class),
mock(JavaAdapter.class),
() -> mSceneInteractor,
mock(StatusBarKeyguardViewManagerInteractor.class),
@@ -759,11 +757,10 @@
mUdfpsOverlayInteractor,
mActivityStarter,
mock(KeyguardTransitionInteractor.class),
+ mock(KeyguardDismissTransitionInteractor.class),
StandardTestDispatcher(null, null),
- () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class),
mock(JavaAdapter.class),
() -> mSceneInteractor,
mock(StatusBarKeyguardViewManagerInteractor.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
index f486787..0945742 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -21,60 +21,61 @@
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.VpnTransportInfo
import android.net.vcn.VcnTransportInfo
import android.net.wifi.WifiInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
-import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
+import com.android.systemui.testKosmos
import com.android.systemui.tuner.TunerService
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class ConnectivityRepositoryImplTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var underTest: ConnectivityRepositoryImpl
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var connectivitySlots: ConnectivitySlots
- @Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var logger: ConnectivityInputLogger
- private lateinit var testScope: TestScope
- @Mock private lateinit var tunerService: TunerService
+ private val connectivityManager = mock<ConnectivityManager>()
+ private val connectivitySlots = mock<ConnectivitySlots>()
+ private val dumpManager = kosmos.dumpManager
+ private val logger = ConnectivityInputLogger(FakeLogBuffer.Factory.create())
+ private val testScope = kosmos.testScope
+ private val tunerService = mock<TunerService>()
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
- testScope = TestScope(UnconfinedTestDispatcher())
createAndSetRepo()
}
@@ -89,12 +90,10 @@
// config_statusBarIconsToExclude when it's first constructed
createAndSetRepo()
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
-
- job.cancel()
}
@Test
@@ -102,14 +101,12 @@
testScope.runTest {
setUpEthernetWifiMobileSlotNames()
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
-
- job.cancel()
}
@Test
@@ -117,19 +114,16 @@
testScope.runTest {
setUpEthernetWifiMobileSlotNames()
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
// WHEN onTuningChanged with the wrong key
getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
- yield()
// THEN we didn't update our value and still have the old one
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
-
- job.cancel()
}
@Test
@@ -143,8 +137,8 @@
// config_statusBarIconsToExclude when it's first constructed
createAndSetRepo()
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
// First, update the slots
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
@@ -152,19 +146,16 @@
// WHEN we update to a null value
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
- yield()
// THEN we go back to our default value
assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
-
- job.cancel()
}
@Test
fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() =
testScope.runTest {
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
@@ -172,8 +163,6 @@
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
-
- job.cancel()
}
@Test
@@ -181,23 +170,21 @@
testScope.runTest {
setUpEthernetWifiMobileSlotNames()
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
// WHEN there's empty and blank slot names
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE, ,,$SLOT_WIFI")
// THEN we skip that slot but still process the other ones
assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
-
- job.cancel()
}
@Test
fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() =
testScope.runTest {
- var latest: Set<ConnectivitySlot>? = null
- val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
@@ -210,8 +197,6 @@
)
assertThat(latest).isEmpty()
-
- job.cancel()
}
@Test
@@ -219,29 +204,25 @@
testScope.runTest {
setUpEthernetWifiMobileSlotNames()
- var latest1: Set<ConnectivitySlot>? = null
- val job1 = underTest.forceHiddenSlots.onEach { latest1 = it }.launchIn(this)
+ val latest1 by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
// WHEN we add a second subscriber after having already emitted a value
- var latest2: Set<ConnectivitySlot>? = null
- val job2 = underTest.forceHiddenSlots.onEach { latest2 = it }.launchIn(this)
+ val latest2 by collectLastValue(underTest.forceHiddenSlots)
+ runCurrent()
// THEN the second subscribe receives the already-emitted value
assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
-
- job1.cancel()
- job2.cancel()
}
@Test
fun defaultConnections_noTransports_nothingIsDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -256,15 +237,12 @@
assertThat(latest!!.wifi.isDefault).isFalse()
assertThat(latest!!.ethernet.isDefault).isFalse()
assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_cellularTransport_mobileIsDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -279,15 +257,12 @@
assertThat(latest!!.wifi.isDefault).isFalse()
assertThat(latest!!.ethernet.isDefault).isFalse()
assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_wifiTransport_wifiIsDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -302,15 +277,12 @@
assertThat(latest!!.ethernet.isDefault).isFalse()
assertThat(latest!!.carrierMerged.isDefault).isFalse()
assertThat(latest!!.mobile.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_ethernetTransport_ethernetIsDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -325,15 +297,12 @@
assertThat(latest!!.wifi.isDefault).isFalse()
assertThat(latest!!.carrierMerged.isDefault).isFalse()
assertThat(latest!!.mobile.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_carrierMergedViaWifi_wifiAndCarrierMergedDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -350,15 +319,12 @@
assertThat(latest!!.wifi.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.mobile.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_carrierMergedViaMobile_mobileCarrierMergedWifiDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -375,15 +341,12 @@
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
-
- job.cancel()
}
@Test
fun defaultConnections_carrierMergedViaWifiWithVcnTransport_wifiAndCarrierMergedDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -400,15 +363,13 @@
assertThat(latest!!.wifi.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.mobile.isDefault).isFalse()
-
- job.cancel()
}
+ /** VCN over W+ (aka VCN over carrier merged). See b/352162710#comment27 scenario #1. */
@Test
fun defaultConnections_carrierMergedViaMobileWithVcnTransport_mobileCarrierMergedWifiDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -425,15 +386,48 @@
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
+ }
- job.cancel()
+ /** VPN over W+ (aka VPN over carrier merged). See b/352162710#comment27 scenario #2. */
+ @Test
+ @EnableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
+ fun defaultConnections_vpnOverCarrierMerged_carrierMergedDefault() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.defaultConnections)
+
+ // Underlying carrier merged network
+ val underlyingCarrierMergedNetwork = mock<Network>()
+ val carrierMergedInfo =
+ mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
+ val underlyingCapabilities =
+ mock<NetworkCapabilities>().also {
+ whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(it.transportInfo).thenReturn(carrierMergedInfo)
+ }
+ whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
+ .thenReturn(underlyingCapabilities)
+
+ val mainCapabilities =
+ mock<NetworkCapabilities>().also {
+ whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
+ // Transports are WIFI|VPN, *not* CELLULAR.
+ whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+ whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+ whenever(it.transportInfo).thenReturn(VpnTransportInfo(0, null, false, false))
+ whenever(it.underlyingNetworks)
+ .thenReturn(listOf(underlyingCarrierMergedNetwork))
+ }
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
+
+ assertThat(latest!!.carrierMerged.isDefault).isTrue()
}
@Test
fun defaultConnections_notCarrierMergedViaWifi_carrierMergedNotDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
@@ -448,15 +442,12 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_notCarrierMergedViaMobile_carrierMergedNotDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val carrierMergedInfo =
mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
@@ -471,15 +462,12 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
- job.cancel()
}
@Test
fun defaultConnections_transportInfoNotWifi_wifiNotDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -492,8 +480,6 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest!!.wifi.isDefault).isFalse()
-
- job.cancel()
}
@Test
@@ -531,8 +517,7 @@
@Test
fun defaultConnections_cellular_underlyingCarrierMergedViaWifi_allDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
// Underlying carrier merged network
val underlyingCarrierMergedNetwork = mock<Network>()
@@ -560,16 +545,17 @@
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
-
- job.cancel()
}
- /** Test for b/225902574. */
+ /**
+ * Test for b/225902574: VPN over VCN over W+ (aka VPN over VCN over carrier merged).
+ *
+ * Also see b/352162710#comment27 scenario #3 and b/352162710#comment30.
+ */
@Test
fun defaultConnections_cellular_underlyingCarrierMergedViaMobileWithVcnTransport_allDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
// Underlying carrier merged network
val underlyingCarrierMergedNetwork = mock<Network>()
@@ -587,6 +573,7 @@
val mainCapabilities =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+ whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
whenever(it.transportInfo).thenReturn(null)
whenever(it.underlyingNetworks)
.thenReturn(listOf(underlyingCarrierMergedNetwork))
@@ -597,15 +584,12 @@
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.carrierMerged.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
-
- job.cancel()
}
@Test
fun defaultConnections_multipleTransports_multipleDefault() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -619,15 +603,12 @@
assertThat(latest!!.mobile.isDefault).isTrue()
assertThat(latest!!.ethernet.isDefault).isTrue()
assertThat(latest!!.wifi.isDefault).isTrue()
-
- job.cancel()
}
@Test
fun defaultConnections_hasValidated_isValidatedTrue() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -638,14 +619,12 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest!!.isValidated).isTrue()
- job.cancel()
}
@Test
fun defaultConnections_noValidated_isValidatedFalse() =
testScope.runTest {
- var latest: DefaultConnectionModel? = null
- val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.defaultConnections)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -656,7 +635,6 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest!!.isValidated).isFalse()
- job.cancel()
}
@Test
@@ -669,8 +647,7 @@
testScope.runTest {
val vcnInfo = VcnTransportInfo(SUB_1_ID)
- var latest: Int? = null
- val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.vcnSubId)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -681,7 +658,6 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest).isEqualTo(SUB_1_ID)
- job.cancel()
}
@Test
@@ -689,8 +665,7 @@
testScope.runTest {
val vcnInfo = VcnTransportInfo(INVALID_SUBSCRIPTION_ID)
- var latest: Int? = null
- val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.vcnSubId)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -701,14 +676,12 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest).isNull()
- job.cancel()
}
@Test
fun vcnSubId_nullIfNoTransportInfo() =
testScope.runTest {
- var latest: Int? = null
- val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.vcnSubId)
val capabilities =
mock<NetworkCapabilities>().also {
@@ -719,7 +692,6 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest).isNull()
- job.cancel()
}
@Test
@@ -728,8 +700,7 @@
// If the underlying network of the VCN is a WiFi network, then there is no subId that
// could disagree with telephony's active data subscription id.
- var latest: Int? = null
- val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.vcnSubId)
val wifiInfo = mock<WifiInfo>()
val vcnInfo = VcnTransportInfo(wifiInfo)
@@ -742,14 +713,12 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest).isNull()
- job.cancel()
}
@Test
fun vcnSubId_changingVcnInfoIsTracked() =
testScope.runTest {
- var latest: Int? = null
- val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.vcnSubId)
val wifiInfo = mock<WifiInfo>()
val wifiVcnInfo = VcnTransportInfo(wifiInfo)
@@ -788,8 +757,6 @@
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
assertThat(latest).isNull()
-
- job.cancel()
}
@Test
@@ -862,6 +829,7 @@
}
@Test
+ @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
fun getMainOrUnderlyingWifiInfo_notCellular_underlyingWifi_noInfo() {
val underlyingNetwork = mock<Network>()
val underlyingWifiInfo = mock<WifiInfo>()
@@ -916,6 +884,7 @@
}
@Test
+ @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
fun getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfo() {
val underlyingNetwork = mock<Network>()
val underlyingVcnInfo = VcnTransportInfo(mock<WifiInfo>())
@@ -1076,12 +1045,13 @@
testScope.backgroundScope,
tunerService,
)
+ testScope.runCurrent()
}
private fun getTunable(): TunerService.Tunable {
val callbackCaptor = argumentCaptor<TunerService.Tunable>()
verify(tunerService).addTunable(callbackCaptor.capture(), any())
- return callbackCaptor.value!!
+ return callbackCaptor.firstValue
}
private fun setUpEthernetWifiMobileSlotNames() {
@@ -1094,7 +1064,7 @@
private fun getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture())
- return callbackCaptor.value!!
+ return callbackCaptor.firstValue
}
private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 12cfdcf..e396b56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.ui.viewmodel
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -33,7 +33,6 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.domain.interactor.keyguardStatusBarInteractor
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.policy.BatteryController
@@ -127,7 +126,7 @@
}
@Test
- @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+ @EnableSceneContainer
fun isVisible_headsUpStatusBarShown_false() =
testScope.runTest {
val latest by collectLastValue(underTest.isVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index caa1779..1e2648b22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -257,13 +257,13 @@
private State createShellState() {
State state = new VolumeDialogController.State();
- for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) {
+ for (int stream : STREAMS.keySet()) {
VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
- ss.name = STREAMS.get(i);
+ ss.name = STREAMS.get(stream);
ss.level = 1;
ss.levelMin = 0;
ss.levelMax = 25;
- state.states.append(i, ss);
+ state.states.append(stream, ss);
}
return state;
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index 1107971..c252924 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -22,7 +22,7 @@
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
-import com.android.systemui.Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR
+import com.android.systemui.Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN
import com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_SYSUI
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@@ -35,7 +35,7 @@
FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
FLAG_KEYGUARD_WM_STATE_REFACTOR,
FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
- FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR,
+ FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
FLAG_PREDICTIVE_BACK_SYSUI,
FLAG_SCENE_CONTAINER,
FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt
new file mode 100644
index 0000000..82a5311
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor by
+ Kosmos.Fixture {
+ KeyguardDismissTransitionInteractor(
+ repository = keyguardTransitionRepository,
+ fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor,
+ fromPrimaryBouncerTransitionInteractor = fromPrimaryBouncerTransitionInteractor,
+ fromAodTransitionInteractor = fromAodTransitionInteractor,
+ fromAlternateBouncerTransitionInteractor = fromAlternateBouncerTransitionInteractor,
+ fromDozingTransitionInteractor = fromDozingTransitionInteractor,
+ fromOccludedTransitionInteractor = fromOccludedTransitionInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
index c6b5ed0..007d229 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
@@ -27,7 +27,7 @@
applicationCoroutineScope,
keyguardRepository,
biometricSettingsRepository,
- keyguardTransitionInteractor,
+ keyguardDismissTransitionInteractor,
internalTransitionInteractor = internalKeyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index 8e8f4b6..aa94c36 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -27,12 +26,6 @@
KeyguardTransitionInteractor(
scope = applicationCoroutineScope,
repository = keyguardTransitionRepository,
- fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
- fromPrimaryBouncerTransitionInteractor = { fromPrimaryBouncerTransitionInteractor },
- fromAodTransitionInteractor = { fromAodTransitionInteractor },
- fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
- fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
- fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor },
sceneInteractor = sceneInteractor
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
index a05e606..4196e54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -25,6 +26,7 @@
val Kosmos.occludedToDozingTransitionViewModel by Fixture {
OccludedToDozingTransitionViewModel(
+ deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
new file mode 100644
index 0000000..c0d65a0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+
+import android.content.packageManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by
+ Kosmos.Fixture {
+ DemoRonChipViewModel(
+ commandRegistry = commandRegistry,
+ packageManager = packageManager,
+ systemClock = fakeSystemClock,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 16e288f..5382c1c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.statusBarChipsLogger
@@ -32,6 +33,7 @@
shareToAppChipViewModel = shareToAppChipViewModel,
castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
callChipViewModel = callChipViewModel,
+ demoRonChipViewModel = demoRonChipViewModel,
logger = statusBarChipsLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt
new file mode 100644
index 0000000..14777b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.commandRegistry: CommandRegistry by
+ Kosmos.Fixture {
+ CommandRegistry(
+ context = applicationContext,
+ // Immediately run anything that comes in
+ mainExecutor = { command -> command.run() },
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt
index 63386d0..dd5bbf3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorKosmos.kt
@@ -18,7 +18,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.volume.data.repository.audioRepository
import com.android.systemui.volume.domain.interactor.audioModeInteractor
import com.android.systemui.volume.mediaOutputInteractor
@@ -27,7 +26,6 @@
AudioSlidersInteractor(
applicationCoroutineScope,
mediaOutputInteractor,
- audioRepository,
audioModeInteractor,
)
}
diff --git a/ravenwood/.gitignore b/ravenwood/.gitignore
new file mode 100644
index 0000000..751553b
--- /dev/null
+++ b/ravenwood/.gitignore
@@ -0,0 +1 @@
+*.bak
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 7e2ee3e..b73f235 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -1,6 +1,3 @@
-// Keep the following two TEST_MAPPINGs in sync:
-// frameworks/base/ravenwood/TEST_MAPPING
-// frameworks/base/tools/hoststubgen/TEST_MAPPING
{
"presubmit": [
{ "name": "tiny-framework-dump-test" },
@@ -39,6 +36,113 @@
}
],
"ravenwood-presubmit": [
+ // AUTO-GENERATED-START
+ // DO NOT MODIFY MANUALLY
+ // Use scripts/update-test-mapping.sh to update it.
+ {
+ "name": "AdServicesSharedLibrariesUnitTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "android.test.mock.ravenwood.tests",
+ "host": true
+ },
+ {
+ "name": "CarLibHostUnitTest",
+ "host": true
+ },
+ {
+ "name": "CarServiceHostUnitTest",
+ "host": true
+ },
+ {
+ "name": "CarSystemUIRavenTests",
+ "host": true
+ },
+ {
+ "name": "CtsAccountManagerTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsAppTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsContentTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsDatabaseTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsGraphicsTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsIcuTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsInputMethodTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsOsTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsProtoTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsResourcesTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsTextTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "CtsUtilTestCasesRavenwood",
+ "host": true
+ },
+ {
+ "name": "FrameworksCoreSystemPropertiesTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "FrameworksCoreTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "FrameworksInputMethodSystemServerTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "FrameworksMockingServicesTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "FrameworksServicesTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "FrameworksUtilTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "InternalTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "PowerStatsTestsRavenwood",
+ "host": true
+ },
+ {
+ "name": "RavenwoodBivalentTest",
+ "host": true
+ },
{
"name": "RavenwoodMinimumTest",
"host": true
@@ -48,16 +152,21 @@
"host": true
},
{
- "name": "CtsUtilTestCasesRavenwood",
- "host": true
- },
- {
"name": "RavenwoodResApkTest",
"host": true
},
{
- "name": "RavenwoodBivalentTest",
+ "name": "RavenwoodRuntimeTest",
+ "host": true
+ },
+ {
+ "name": "RavenwoodServicesTest",
+ "host": true
+ },
+ {
+ "name": "SystemUiRavenTests",
"host": true
}
+ // AUTO-GENERATED-END
]
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 2b55ac5..8271039 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -21,6 +21,8 @@
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
+import android.util.Log;
+
import com.android.ravenwood.common.RavenwoodCommonUtils;
import com.android.ravenwood.common.SneakyThrow;
@@ -36,6 +38,7 @@
import org.junit.runner.manipulation.Orderer;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.Statement;
@@ -134,8 +137,10 @@
return runner;
}
- private final TestClass mTestClsas;
- private final Runner mRealRunner;
+ private TestClass mTestClass = null;
+ private Runner mRealRunner = null;
+ private Description mDescription = null;
+ private Throwable mExceptionInConstructor = null;
/** Simple logging method. */
private void log(String message) {
@@ -149,45 +154,62 @@
}
public TestClass getTestClass() {
- return mTestClsas;
+ return mTestClass;
}
/**
* Constructor.
*/
public RavenwoodAwareTestRunner(Class<?> testClass) {
- mTestClsas = new TestClass(testClass);
-
- /*
- * If the class has @DisabledOnRavenwood, then we'll delegate to ClassSkippingTestRunner,
- * which simply skips it.
- */
- if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
- mTestClsas.getJavaClass())) {
- mRealRunner = new ClassSkippingTestRunner(mTestClsas);
- return;
- }
-
- // Find the real runner.
- final Class<? extends Runner> realRunner;
- final InnerRunner innerRunnerAnnotation = mTestClsas.getAnnotation(InnerRunner.class);
- if (innerRunnerAnnotation != null) {
- realRunner = innerRunnerAnnotation.value();
- } else {
- // Default runner.
- realRunner = BlockJUnit4ClassRunner.class;
- }
-
- onRunnerInitializing();
-
try {
- log("Initializing the inner runner: " + realRunner);
+ mTestClass = new TestClass(testClass);
- mRealRunner = realRunner.getConstructor(Class.class).newInstance(testClass);
+ /*
+ * If the class has @DisabledOnRavenwood, then we'll delegate to
+ * ClassSkippingTestRunner, which simply skips it.
+ */
+ if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
+ mTestClass.getJavaClass())) {
+ mRealRunner = new ClassSkippingTestRunner(mTestClass);
+ mDescription = mRealRunner.getDescription();
+ return;
+ }
- } catch (InstantiationException | IllegalAccessException
- | InvocationTargetException | NoSuchMethodException e) {
- throw logAndFail("Failed to instantiate " + realRunner, e);
+ // Find the real runner.
+ final Class<? extends Runner> realRunner;
+ final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class);
+ if (innerRunnerAnnotation != null) {
+ realRunner = innerRunnerAnnotation.value();
+ } else {
+ // Default runner.
+ realRunner = BlockJUnit4ClassRunner.class;
+ }
+
+ onRunnerInitializing();
+
+ try {
+ log("Initializing the inner runner: " + realRunner);
+
+ mRealRunner = realRunner.getConstructor(Class.class).newInstance(testClass);
+ mDescription = mRealRunner.getDescription();
+
+ } catch (InstantiationException | IllegalAccessException
+ | InvocationTargetException | NoSuchMethodException e) {
+ throw logAndFail("Failed to instantiate " + realRunner, e);
+ }
+ } catch (Throwable th) {
+ // If we throw in the constructor, Tradefed may not report it and just ignore the class,
+ // so record it and throw it when the test actually started.
+ log("Fatal: Exception detected in constructor: " + th.getMessage() + "\n"
+ + Log.getStackTraceString(th));
+ mExceptionInConstructor = new RuntimeException("Exception detected in constructor",
+ th);
+ mDescription = Description.createTestDescription(testClass, "Constructor");
+
+ // This is for testing if tradefed is fixed.
+ if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) {
+ throw th;
+ }
}
}
@@ -202,7 +224,7 @@
log("onRunnerInitializing");
- RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClsas);
+ RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass);
// Hook point to allow more customization.
runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
@@ -230,7 +252,7 @@
@Override
public Description getDescription() {
- return mRealRunner.getDescription();
+ return mDescription;
}
@Override
@@ -241,6 +263,10 @@
return;
}
+ if (maybeReportExceptionFromConstructor(notifier)) {
+ return;
+ }
+
sCurrentRunner.set(this);
try {
runWithHooks(getDescription(), Scope.Runner, Order.First,
@@ -250,6 +276,18 @@
}
}
+ /** Throw the exception detected in the constructor, if any. */
+ private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) {
+ if (mExceptionInConstructor == null) {
+ return false;
+ }
+ notifier.fireTestStarted(mDescription);
+ notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor));
+ notifier.fireTestFinished(mDescription);
+
+ return true;
+ }
+
@Override
public void filter(Filter filter) throws NoTestsRemainException {
if (mRealRunner instanceof Filterable r) {
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java
new file mode 100644
index 0000000..478503b
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2019 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 libcore.util;
+
+/**
+ * <p>The {@code FP16} class is a wrapper and a utility class to manipulate half-precision 16-bit
+ * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
+ * floating point data types (also called fp16 or binary16). A half-precision float can be
+ * created from or converted to single-precision floats, and is stored in a short data type.
+ *
+ * <p>The IEEE 754 standard specifies an fp16 as having the following format:</p>
+ * <ul>
+ * <li>Sign bit: 1 bit</li>
+ * <li>Exponent width: 5 bits</li>
+ * <li>Significand: 10 bits</li>
+ * </ul>
+ *
+ * <p>The format is laid out as follows:</p>
+ * <pre>
+ * 1 11111 1111111111
+ * ^ --^-- -----^----
+ * sign | |_______ significand
+ * |
+ * -- exponent
+ * </pre>
+ *
+ * <p>Half-precision floating points can be useful to save memory and/or
+ * bandwidth at the expense of range and precision when compared to single-precision
+ * floating points (fp32).</p>
+ * <p>To help you decide whether fp16 is the right storage type for you need, please
+ * refer to the table below that shows the available precision throughout the range of
+ * possible values. The <em>precision</em> column indicates the step size between two
+ * consecutive numbers in a specific part of the range.</p>
+ *
+ * <table summary="Precision of fp16 across the range">
+ * <tr><th>Range start</th><th>Precision</th></tr>
+ * <tr><td>0</td><td>1 ⁄ 16,777,216</td></tr>
+ * <tr><td>1 ⁄ 16,384</td><td>1 ⁄ 16,777,216</td></tr>
+ * <tr><td>1 ⁄ 8,192</td><td>1 ⁄ 8,388,608</td></tr>
+ * <tr><td>1 ⁄ 4,096</td><td>1 ⁄ 4,194,304</td></tr>
+ * <tr><td>1 ⁄ 2,048</td><td>1 ⁄ 2,097,152</td></tr>
+ * <tr><td>1 ⁄ 1,024</td><td>1 ⁄ 1,048,576</td></tr>
+ * <tr><td>1 ⁄ 512</td><td>1 ⁄ 524,288</td></tr>
+ * <tr><td>1 ⁄ 256</td><td>1 ⁄ 262,144</td></tr>
+ * <tr><td>1 ⁄ 128</td><td>1 ⁄ 131,072</td></tr>
+ * <tr><td>1 ⁄ 64</td><td>1 ⁄ 65,536</td></tr>
+ * <tr><td>1 ⁄ 32</td><td>1 ⁄ 32,768</td></tr>
+ * <tr><td>1 ⁄ 16</td><td>1 ⁄ 16,384</td></tr>
+ * <tr><td>1 ⁄ 8</td><td>1 ⁄ 8,192</td></tr>
+ * <tr><td>1 ⁄ 4</td><td>1 ⁄ 4,096</td></tr>
+ * <tr><td>1 ⁄ 2</td><td>1 ⁄ 2,048</td></tr>
+ * <tr><td>1</td><td>1 ⁄ 1,024</td></tr>
+ * <tr><td>2</td><td>1 ⁄ 512</td></tr>
+ * <tr><td>4</td><td>1 ⁄ 256</td></tr>
+ * <tr><td>8</td><td>1 ⁄ 128</td></tr>
+ * <tr><td>16</td><td>1 ⁄ 64</td></tr>
+ * <tr><td>32</td><td>1 ⁄ 32</td></tr>
+ * <tr><td>64</td><td>1 ⁄ 16</td></tr>
+ * <tr><td>128</td><td>1 ⁄ 8</td></tr>
+ * <tr><td>256</td><td>1 ⁄ 4</td></tr>
+ * <tr><td>512</td><td>1 ⁄ 2</td></tr>
+ * <tr><td>1,024</td><td>1</td></tr>
+ * <tr><td>2,048</td><td>2</td></tr>
+ * <tr><td>4,096</td><td>4</td></tr>
+ * <tr><td>8,192</td><td>8</td></tr>
+ * <tr><td>16,384</td><td>16</td></tr>
+ * <tr><td>32,768</td><td>32</td></tr>
+ * </table>
+ *
+ * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
+ *
+ * @hide
+ */
+
+public final class FP16 {
+ /**
+ * The number of bits used to represent a half-precision float value.
+ *
+ * @hide
+ */
+ public static final int SIZE = 16;
+
+ /**
+ * Epsilon is the difference between 1.0 and the next value representable
+ * by a half-precision floating-point.
+ *
+ * @hide
+ */
+ public static final short EPSILON = (short) 0x1400;
+
+ /**
+ * Maximum exponent a finite half-precision float may have.
+ *
+ * @hide
+ */
+ public static final int MAX_EXPONENT = 15;
+ /**
+ * Minimum exponent a normalized half-precision float may have.
+ *
+ * @hide
+ */
+ public static final int MIN_EXPONENT = -14;
+
+ /**
+ * Smallest negative value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short LOWEST_VALUE = (short) 0xfbff;
+ /**
+ * Maximum positive finite value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short MAX_VALUE = (short) 0x7bff;
+ /**
+ * Smallest positive normal value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short MIN_NORMAL = (short) 0x0400;
+ /**
+ * Smallest positive non-zero value a half-precision float may have.
+ *
+ * @hide
+ */
+ public static final short MIN_VALUE = (short) 0x0001;
+ /**
+ * A Not-a-Number representation of a half-precision float.
+ *
+ * @hide
+ */
+ public static final short NaN = (short) 0x7e00;
+ /**
+ * Negative infinity of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short NEGATIVE_INFINITY = (short) 0xfc00;
+ /**
+ * Negative 0 of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short NEGATIVE_ZERO = (short) 0x8000;
+ /**
+ * Positive infinity of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short POSITIVE_INFINITY = (short) 0x7c00;
+ /**
+ * Positive 0 of type half-precision float.
+ *
+ * @hide
+ */
+ public static final short POSITIVE_ZERO = (short) 0x0000;
+
+ /**
+ * The offset to shift by to obtain the sign bit.
+ *
+ * @hide
+ */
+ public static final int SIGN_SHIFT = 15;
+
+ /**
+ * The offset to shift by to obtain the exponent bits.
+ *
+ * @hide
+ */
+ public static final int EXPONENT_SHIFT = 10;
+
+ /**
+ * The bitmask to AND a number with to obtain the sign bit.
+ *
+ * @hide
+ */
+ public static final int SIGN_MASK = 0x8000;
+
+ /**
+ * The bitmask to AND a number shifted by {@link #EXPONENT_SHIFT} right, to obtain exponent bits.
+ *
+ * @hide
+ */
+ public static final int SHIFTED_EXPONENT_MASK = 0x1f;
+
+ /**
+ * The bitmask to AND a number with to obtain significand bits.
+ *
+ * @hide
+ */
+ public static final int SIGNIFICAND_MASK = 0x3ff;
+
+ /**
+ * The bitmask to AND with to obtain exponent and significand bits.
+ *
+ * @hide
+ */
+ public static final int EXPONENT_SIGNIFICAND_MASK = 0x7fff;
+
+ /**
+ * The offset of the exponent from the actual value.
+ *
+ * @hide
+ */
+ public static final int EXPONENT_BIAS = 15;
+
+ private static final int FP32_SIGN_SHIFT = 31;
+ private static final int FP32_EXPONENT_SHIFT = 23;
+ private static final int FP32_SHIFTED_EXPONENT_MASK = 0xff;
+ private static final int FP32_SIGNIFICAND_MASK = 0x7fffff;
+ private static final int FP32_EXPONENT_BIAS = 127;
+ private static final int FP32_QNAN_MASK = 0x400000;
+ private static final int FP32_DENORMAL_MAGIC = 126 << 23;
+ private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
+
+ /** Hidden constructor to prevent instantiation. */
+ private FP16() {}
+
+ /**
+ * <p>Compares the two specified half-precision float values. The following
+ * conditions apply during the comparison:</p>
+ *
+ * <ul>
+ * <li>{@link #NaN} is considered by this method to be equal to itself and greater
+ * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li>
+ * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than
+ * {@link #NEGATIVE_ZERO}.</li>
+ * </ul>
+ *
+ * @param x The first half-precision float value to compare.
+ * @param y The second half-precision float value to compare
+ *
+ * @return The value {@code 0} if {@code x} is numerically equal to {@code y}, a
+ * value less than {@code 0} if {@code x} is numerically less than {@code y},
+ * and a value greater than {@code 0} if {@code x} is numerically greater
+ * than {@code y}
+ *
+ * @hide
+ */
+ public static int compare(short x, short y) {
+ if (less(x, y)) return -1;
+ if (greater(x, y)) return 1;
+
+ // Collapse NaNs, akin to halfToIntBits(), but we want to keep
+ // (signed) short value types to preserve the ordering of -0.0
+ // and +0.0
+ short xBits = isNaN(x) ? NaN : x;
+ short yBits = isNaN(y) ? NaN : y;
+
+ return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+ }
+
+ /**
+ * Returns the closest integral half-precision float value to the specified
+ * half-precision float value. Special values are handled in the
+ * following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The value of the specified half-precision float rounded to the nearest
+ * half-precision float value
+ *
+ * @hide
+ */
+ public static short rint(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ if (abs > 0x3800){
+ result |= 0x3c00;
+ }
+ } else if (abs < 0x6400) {
+ int exp = 25 - (abs >> 10);
+ int mask = (1 << exp) - 1;
+ result += ((1 << (exp - 1)) - (~(abs >> exp) & 1));
+ result &= ~mask;
+ }
+ if (isNaN((short) result)) {
+ // if result is NaN mask with qNaN
+ // (i.e. mask the most significant mantissa bit with 1)
+ // to comply with hardware implementations (ARM64, Intel, etc).
+ result |= NaN;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the smallest half-precision float value toward negative infinity
+ * greater than or equal to the specified half-precision float value.
+ * Special values are handled in the following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The smallest half-precision float value toward negative infinity
+ * greater than or equal to the specified half-precision float value
+ *
+ * @hide
+ */
+ public static short ceil(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ result |= 0x3c00 & -(~(bits >> 15) & (abs != 0 ? 1 : 0));
+ } else if (abs < 0x6400) {
+ abs = 25 - (abs >> 10);
+ int mask = (1 << abs) - 1;
+ result += mask & ((bits >> 15) - 1);
+ result &= ~mask;
+ }
+ if (isNaN((short) result)) {
+ // if result is NaN mask with qNaN
+ // (i.e. mask the most significant mantissa bit with 1)
+ // to comply with hardware implementations (ARM64, Intel, etc).
+ result |= NaN;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the largest half-precision float value toward positive infinity
+ * less than or equal to the specified half-precision float value.
+ * Special values are handled in the following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The largest half-precision float value toward positive infinity
+ * less than or equal to the specified half-precision float value
+ *
+ * @hide
+ */
+ public static short floor(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
+ } else if (abs < 0x6400) {
+ abs = 25 - (abs >> 10);
+ int mask = (1 << abs) - 1;
+ result += mask & -(bits >> 15);
+ result &= ~mask;
+ }
+ if (isNaN((short) result)) {
+ // if result is NaN mask with qNaN
+ // i.e. (Mask the most significant mantissa bit with 1)
+ result |= NaN;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the truncated half-precision float value of the specified
+ * half-precision float value. Special values are handled in the following ways:
+ * <ul>
+ * <li>If the specified half-precision float is NaN, the result is NaN</li>
+ * <li>If the specified half-precision float is infinity (negative or positive),
+ * the result is infinity (with the same sign)</li>
+ * <li>If the specified half-precision float is zero (negative or positive),
+ * the result is zero (with the same sign)</li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return The truncated half-precision float value of the specified
+ * half-precision float value
+ *
+ * @hide
+ */
+ public static short trunc(short h) {
+ int bits = h & 0xffff;
+ int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+ int result = bits;
+
+ if (abs < 0x3c00) {
+ result &= SIGN_MASK;
+ } else if (abs < 0x6400) {
+ abs = 25 - (abs >> 10);
+ int mask = (1 << abs) - 1;
+ result &= ~mask;
+ }
+
+ return (short) result;
+ }
+
+ /**
+ * Returns the smaller of two half-precision float values (the value closest
+ * to negative infinity). Special values are handled in the following ways:
+ * <ul>
+ * <li>If either value is NaN, the result is NaN</li>
+ * <li>{@link #NEGATIVE_ZERO} is smaller than {@link #POSITIVE_ZERO}</li>
+ * </ul>
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ * @return The smaller of the two specified half-precision values
+ *
+ * @hide
+ */
+ public static short min(short x, short y) {
+ if (isNaN(x)) return NaN;
+ if (isNaN(y)) return NaN;
+
+ if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) {
+ return (x & SIGN_MASK) != 0 ? x : y;
+ }
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ }
+
+ /**
+ * Returns the larger of two half-precision float values (the value closest
+ * to positive infinity). Special values are handled in the following ways:
+ * <ul>
+ * <li>If either value is NaN, the result is NaN</li>
+ * <li>{@link #POSITIVE_ZERO} is greater than {@link #NEGATIVE_ZERO}</li>
+ * </ul>
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return The larger of the two specified half-precision values
+ *
+ * @hide
+ */
+ public static short max(short x, short y) {
+ if (isNaN(x)) return NaN;
+ if (isNaN(y)) return NaN;
+
+ if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) {
+ return (x & SIGN_MASK) != 0 ? y : x;
+ }
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ }
+
+ /**
+ * Returns true if the first half-precision float value is less (smaller
+ * toward negative infinity) than the second half-precision float value.
+ * If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is less than y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean less(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the first half-precision float value is less (smaller
+ * toward negative infinity) than or equal to the second half-precision
+ * float value. If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is less than or equal to y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean lessEquals(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the first half-precision float value is greater (larger
+ * toward positive infinity) than the second half-precision float value.
+ * If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is greater than y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean greater(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the first half-precision float value is greater (larger
+ * toward positive infinity) than or equal to the second half-precision float
+ * value. If either of the values is NaN, the result is false.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is greater than y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean greaterEquals(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
+ ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ }
+
+ /**
+ * Returns true if the two half-precision float values are equal.
+ * If either of the values is NaN, the result is false. {@link #POSITIVE_ZERO}
+ * and {@link #NEGATIVE_ZERO} are considered equal.
+ *
+ * @param x The first half-precision value
+ * @param y The second half-precision value
+ *
+ * @return True if x is equal to y, false otherwise
+ *
+ * @hide
+ */
+ public static boolean equals(short x, short y) {
+ if (isNaN(x)) return false;
+ if (isNaN(y)) return false;
+
+ return x == y || ((x | y) & EXPONENT_SIGNIFICAND_MASK) == 0;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value represents
+ * infinity, false otherwise.
+ *
+ * @param h A half-precision float value
+ * @return True if the value is positive infinity or negative infinity,
+ * false otherwise
+ *
+ * @hide
+ */
+ public static boolean isInfinite(short h) {
+ return (h & EXPONENT_SIGNIFICAND_MASK) == POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value represents
+ * a Not-a-Number, false otherwise.
+ *
+ * @param h A half-precision float value
+ * @return True if the value is a NaN, false otherwise
+ *
+ * @hide
+ */
+ public static boolean isNaN(short h) {
+ return (h & EXPONENT_SIGNIFICAND_MASK) > POSITIVE_INFINITY;
+ }
+
+ /**
+ * Returns true if the specified half-precision float value is normalized
+ * (does not have a subnormal representation). If the specified value is
+ * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY},
+ * {@link #POSITIVE_ZERO}, {@link #NEGATIVE_ZERO}, NaN or any subnormal
+ * number, this method returns false.
+ *
+ * @param h A half-precision float value
+ * @return True if the value is normalized, false otherwise
+ *
+ * @hide
+ */
+ public static boolean isNormalized(short h) {
+ return (h & POSITIVE_INFINITY) != 0 && (h & POSITIVE_INFINITY) != POSITIVE_INFINITY;
+ }
+
+ /**
+ * <p>Converts the specified half-precision float value into a
+ * single-precision float value. The following special cases are handled:</p>
+ * <ul>
+ * <li>If the input is {@link #NaN}, the returned value is {@link Float#NaN}</li>
+ * <li>If the input is {@link #POSITIVE_INFINITY} or
+ * {@link #NEGATIVE_INFINITY}, the returned value is respectively
+ * {@link Float#POSITIVE_INFINITY} or {@link Float#NEGATIVE_INFINITY}</li>
+ * <li>If the input is 0 (positive or negative), the returned value is +/-0.0f</li>
+ * <li>Otherwise, the returned value is a normalized single-precision float value</li>
+ * </ul>
+ *
+ * @param h The half-precision float value to convert to single-precision
+ * @return A normalized single-precision float value
+ *
+ * @hide
+ */
+ public static float toFloat(short h) {
+ int bits = h & 0xffff;
+ int s = bits & SIGN_MASK;
+ int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK;
+ int m = (bits ) & SIGNIFICAND_MASK;
+
+ int outE = 0;
+ int outM = 0;
+
+ if (e == 0) { // Denormal or 0
+ if (m != 0) {
+ // Convert denorm fp16 into normalized fp32
+ float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
+ o -= FP32_DENORMAL_FLOAT;
+ return s == 0 ? o : -o;
+ }
+ } else {
+ outM = m << 13;
+ if (e == 0x1f) { // Infinite or NaN
+ outE = 0xff;
+ if (outM != 0) { // SNaNs are quieted
+ outM |= FP32_QNAN_MASK;
+ }
+ } else {
+ outE = e - EXPONENT_BIAS + FP32_EXPONENT_BIAS;
+ }
+ }
+
+ int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
+ return Float.intBitsToFloat(out);
+ }
+
+ /**
+ * <p>Converts the specified single-precision float value into a
+ * half-precision float value. The following special cases are handled:</p>
+ * <ul>
+ * <li>If the input is NaN (see {@link Float#isNaN(float)}), the returned
+ * value is {@link #NaN}</li>
+ * <li>If the input is {@link Float#POSITIVE_INFINITY} or
+ * {@link Float#NEGATIVE_INFINITY}, the returned value is respectively
+ * {@link #POSITIVE_INFINITY} or {@link #NEGATIVE_INFINITY}</li>
+ * <li>If the input is 0 (positive or negative), the returned value is
+ * {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+ * <li>If the input is a less than {@link #MIN_VALUE}, the returned value
+ * is flushed to {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+ * <li>If the input is a less than {@link #MIN_NORMAL}, the returned value
+ * is a denorm half-precision float</li>
+ * <li>Otherwise, the returned value is rounded to the nearest
+ * representable half-precision float value</li>
+ * </ul>
+ *
+ * @param f The single-precision float value to convert to half-precision
+ * @return A half-precision float value
+ *
+ * @hide
+ */
+ public static short toHalf(float f) {
+ int bits = Float.floatToRawIntBits(f);
+ int s = (bits >>> FP32_SIGN_SHIFT );
+ int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_SHIFTED_EXPONENT_MASK;
+ int m = (bits ) & FP32_SIGNIFICAND_MASK;
+
+ int outE = 0;
+ int outM = 0;
+
+ if (e == 0xff) { // Infinite or NaN
+ outE = 0x1f;
+ outM = m != 0 ? 0x200 : 0;
+ } else {
+ e = e - FP32_EXPONENT_BIAS + EXPONENT_BIAS;
+ if (e >= 0x1f) { // Overflow
+ outE = 0x1f;
+ } else if (e <= 0) { // Underflow
+ if (e < -10) {
+ // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
+ } else {
+ // The fp32 value is a normalized float less than MIN_NORMAL,
+ // we convert to a denorm fp16
+ m = m | 0x800000;
+ int shift = 14 - e;
+ outM = m >> shift;
+
+ int lowm = m & ((1 << shift) - 1);
+ int hway = 1 << (shift - 1);
+ // if above halfway or exactly halfway and outM is odd
+ if (lowm + (outM & 1) > hway){
+ // Round to nearest even
+ // Can overflow into exponent bit, which surprisingly is OK.
+ // This increment relies on the +outM in the return statement below
+ outM++;
+ }
+ }
+ } else {
+ outE = e;
+ outM = m >> 13;
+ // if above halfway or exactly halfway and outM is odd
+ if ((m & 0x1fff) + (outM & 0x1) > 0x1000) {
+ // Round to nearest even
+ // Can overflow into exponent bit, which surprisingly is OK.
+ // This increment relies on the +outM in the return statement below
+ outM++;
+ }
+ }
+ }
+ // The outM is added here as the +1 increments for outM above can
+ // cause an overflow in the exponent bit which is OK.
+ return (short) ((s << SIGN_SHIFT) | (outE << EXPONENT_SHIFT) + outM);
+ }
+
+ /**
+ * <p>Returns a hexadecimal string representation of the specified half-precision
+ * float value. If the value is a NaN, the result is <code>"NaN"</code>,
+ * otherwise the result follows this format:</p>
+ * <ul>
+ * <li>If the sign is positive, no sign character appears in the result</li>
+ * <li>If the sign is negative, the first character is <code>'-'</code></li>
+ * <li>If the value is inifinity, the string is <code>"Infinity"</code></li>
+ * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li>
+ * <li>If the value has a normalized representation, the exponent and
+ * significand are represented in the string in two fields. The significand
+ * starts with <code>"0x1."</code> followed by its lowercase hexadecimal
+ * representation. Trailing zeroes are removed unless all digits are 0, then
+ * a single zero is used. The significand representation is followed by the
+ * exponent, represented by <code>"p"</code>, itself followed by a decimal
+ * string of the unbiased exponent</li>
+ * <li>If the value has a subnormal representation, the significand starts
+ * with <code>"0x0."</code> followed by its lowercase hexadecimal
+ * representation. Trailing zeroes are removed unless all digits are 0, then
+ * a single zero is used. The significand representation is followed by the
+ * exponent, represented by <code>"p-14"</code></li>
+ * </ul>
+ *
+ * @param h A half-precision float value
+ * @return A hexadecimal string representation of the specified value
+ *
+ * @hide
+ */
+ public static String toHexString(short h) {
+ StringBuilder o = new StringBuilder();
+
+ int bits = h & 0xffff;
+ int s = (bits >>> SIGN_SHIFT );
+ int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK;
+ int m = (bits ) & SIGNIFICAND_MASK;
+
+ if (e == 0x1f) { // Infinite or NaN
+ if (m == 0) {
+ if (s != 0) o.append('-');
+ o.append("Infinity");
+ } else {
+ o.append("NaN");
+ }
+ } else {
+ if (s == 1) o.append('-');
+ if (e == 0) {
+ if (m == 0) {
+ o.append("0x0.0p0");
+ } else {
+ o.append("0x0.");
+ String significand = Integer.toHexString(m);
+ o.append(significand.replaceFirst("0{2,}$", ""));
+ o.append("p-14");
+ }
+ } else {
+ o.append("0x1.");
+ String significand = Integer.toHexString(m);
+ o.append(significand.replaceFirst("0{2,}$", ""));
+ o.append('p');
+ o.append(Integer.toString(e - EXPONENT_BIAS));
+ }
+ }
+
+ return o.toString();
+ }
+}
diff --git a/ravenwood/scripts/list-ravenwood-tests.sh b/ravenwood/scripts/list-ravenwood-tests.sh
index fb9b823..05f3fdf 100755
--- a/ravenwood/scripts/list-ravenwood-tests.sh
+++ b/ravenwood/scripts/list-ravenwood-tests.sh
@@ -15,4 +15,4 @@
# List all the ravenwood test modules.
-jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json"
+jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json" | sort
diff --git a/ravenwood/scripts/remove-ravenizer-output.sh b/ravenwood/scripts/remove-ravenizer-output.sh
new file mode 100755
index 0000000..be15b71
--- /dev/null
+++ b/ravenwood/scripts/remove-ravenizer-output.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# 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.
+
+# Delete all the ravenizer output jar files from Soong's intermediate directory.
+
+# `-a -prune` is needed because otherwise find would be confused if the directory disappears.
+
+find "${ANDROID_BUILD_TOP:?}/out/soong/.intermediates/" \
+ -type d \
+ -name 'ravenizer' \
+ -print \
+ -exec rm -fr \{\} \; \
+ -a -prune
diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh
new file mode 100755
index 0000000..b6cf5b8
--- /dev/null
+++ b/ravenwood/scripts/update-test-mapping.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+# 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.
+
+# Update f/b/r/TEST_MAPPING with all the ravenwood tests as presubmit.
+#
+# Note, before running it, make sure module-info.json is up-to-date by running
+# (any) build.
+
+set -e
+
+main() {
+ local script_name="${0##*/}"
+ local script_dir="${0%/*}"
+ local test_mapping="$script_dir/../TEST_MAPPING"
+ local test_mapping_bak="$script_dir/../TEST_MAPPING.bak"
+
+ local header="$(sed -ne '1,/AUTO-GENERATED-START/p' "$test_mapping")"
+ local footer="$(sed -ne '/AUTO-GENERATED-END/,$p' "$test_mapping")"
+
+ echo "Getting all tests"
+ local tests=( $("$script_dir/list-ravenwood-tests.sh") )
+
+ local num_tests="${#tests[@]}"
+
+ if (( $num_tests == 0 )) ; then
+ echo "Something went wrong. No ravenwood tests detected." 1>&2
+ return 1
+ fi
+
+ echo "Tests: ${tests[@]}"
+
+ echo "Creating backup at $test_mapping_bak"
+ cp "$test_mapping" "$test_mapping_bak"
+
+ echo "Updating $test_mapping"
+ {
+ echo "$header"
+
+ echo " // DO NOT MODIFY MANUALLY"
+ echo " // Use scripts/$script_name to update it."
+
+ local i=0
+ while (( $i < $num_tests )) ; do
+ local comma=","
+ if (( $i == ($num_tests - 1) )); then
+ comma=""
+ fi
+ echo " {"
+ echo " \"name\": \"${tests[$i]}\","
+ echo " \"host\": true"
+ echo " }$comma"
+
+ i=$(( $i + 1 ))
+ done
+
+ echo "$footer"
+ } >"$test_mapping"
+
+ if cmp "$test_mapping_bak" "$test_mapping" ; then
+ echo "No change detecetd."
+ return 0
+ fi
+ echo "Updated $test_mapping"
+
+ # `|| true` is needed because of `set -e`.
+ diff -u "$test_mapping_bak" "$test_mapping" || true
+ return 0
+}
+
+main
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index d8366c5..34239b8 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -46,6 +46,7 @@
android.util.EventLog
android.util.FloatProperty
android.util.FloatMath
+android.util.Half
android.util.IndentingPrintWriter
android.util.IntArray
android.util.IntProperty
@@ -277,7 +278,11 @@
android.graphics.Insets
android.graphics.Interpolator
android.graphics.Matrix
+android.graphics.Matrix44
+android.graphics.Outline
+android.graphics.ParcelableColorSpace
android.graphics.Path
+android.graphics.PixelFormat
android.graphics.Point
android.graphics.PointF
android.graphics.Rect
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
index 3a7fab3..0dcd271 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
@@ -17,7 +17,14 @@
package com.android.platform.test.ravenwood.ravenizer
+import com.android.hoststubgen.UserErrorException
+
/**
* Use it for internal exception that really shouldn't happen.
*/
class RavenizerInternalException(message: String) : Exception(message)
+
+/**
+ * Thrown when an invalid test is detected in the target jar. (e.g. JUni3 tests)
+ */
+class RavenizerInvalidTestException(message: String) : Exception(message), UserErrorException
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
index e92ef72..a38512e 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
@@ -44,6 +44,9 @@
/** Time took to build [ClasNodes] */
var loadStructureTime: Double = .0,
+ /** Time took to validate the classes */
+ var validationTime: Double = .0,
+
/** Total real time spent for converting the jar file */
var totalProcessTime: Double = .0,
@@ -67,6 +70,7 @@
RavenizerStats{
totalTime=$totalTime,
loadStructureTime=$loadStructureTime,
+ validationTime=$validationTime,
totalProcessTime=$totalProcessTime,
totalConversionTime=$totalConversionTime,
totalCopyTime=$totalCopyTime,
@@ -84,16 +88,44 @@
class Ravenizer(val options: RavenizerOptions) {
fun run() {
val stats = RavenizerStats()
+
+ val fatalValidation = options.fatalValidation.get
+
stats.totalTime = log.nTime {
- process(options.inJar.get, options.outJar.get, stats)
+ process(
+ options.inJar.get,
+ options.outJar.get,
+ options.enableValidation.get,
+ fatalValidation,
+ stats,
+ )
}
log.i(stats.toString())
}
- private fun process(inJar: String, outJar: String, stats: RavenizerStats) {
+ private fun process(
+ inJar: String,
+ outJar: String,
+ enableValidation: Boolean,
+ fatalValidation: Boolean,
+ stats: RavenizerStats,
+ ) {
var allClasses = ClassNodes.loadClassStructures(inJar) {
time -> stats.loadStructureTime = time
}
+ if (enableValidation) {
+ stats.validationTime = log.iTime("Validating classes") {
+ if (!validateClasses(allClasses)) {
+ var message = "Invalid test class(es) detected." +
+ " See error log for details."
+ if (fatalValidation) {
+ throw RavenizerInvalidTestException(message)
+ } else {
+ log.w("Warning: $message")
+ }
+ }
+ }
+ }
stats.totalProcessTime = log.iTime("$executableName processing $inJar") {
ZipFile(inJar).use { inZip ->
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
index e85e3be..e8341e5 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
@@ -27,6 +27,12 @@
/** Output jar file */
var outJar: SetOnce<String> = SetOnce(""),
+
+ /** Whether to enable test validation. */
+ var enableValidation: SetOnce<Boolean> = SetOnce(true),
+
+ /** Whether the validation failure is fatal or not. */
+ var fatalValidation: SetOnce<Boolean> = SetOnce(false),
) {
companion object {
fun parseArgs(args: Array<String>): RavenizerOptions {
@@ -52,6 +58,12 @@
"--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
"--out-jar" -> ret.outJar.set(nextArg())
+ "--enable-validation" -> ret.enableValidation.set(true)
+ "--disable-validation" -> ret.enableValidation.set(false)
+
+ "--fatal-validation" -> ret.fatalValidation.set(true)
+ "--no-fatal-validation" -> ret.fatalValidation.set(false)
+
else -> throw ArgumentsException("Unknown option: $arg")
}
} catch (e: SetOnce.SetMoreThanOnceException) {
@@ -74,6 +86,8 @@
RavenizerOptions{
inJar=$inJar,
outJar=$outJar,
+ enableValidation=$enableValidation,
+ fatalValidation=$fatalValidation,
}
""".trimIndent()
}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
index e026e7a..059f5a4 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
@@ -87,9 +87,12 @@
return this.startsWithAny(
"java/", // just in case...
"javax/",
+ "junit/",
"org/junit/",
"org/mockito/",
"kotlin/",
+ "androidx/",
+ "android/support/",
// TODO -- anything else?
)
}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
new file mode 100644
index 0000000..27092d2
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.startsWithAny
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.log
+import org.objectweb.asm.tree.ClassNode
+
+fun validateClasses(classes: ClassNodes): Boolean {
+ var allOk = true
+ classes.forEach { allOk = checkClass(it, classes) && allOk }
+
+ return allOk
+}
+
+/**
+ * Validate a class.
+ *
+ * - A test class shouldn't extend
+ *
+ */
+fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean {
+ if (cn.name.shouldByBypassed()) {
+ // Class doesn't need to be checked.
+ return true
+ }
+ var allOk = true
+
+ // See if there's any class that extends a legacy base class.
+ // But ignore the base classes in android.test.
+ if (!cn.name.startsWithAny("android/test/")) {
+ allOk = checkSuperClass(cn, cn, classes) && allOk
+ }
+ return allOk
+}
+
+fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean {
+ if (currentClass.superName == null || currentClass.superName == "java/lang/Object") {
+ return true // No parent class
+ }
+ if (currentClass.superName.isLegacyTestBaseClass()) {
+ log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends"
+ + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.")
+ return false
+ }
+ classes.findClass(currentClass.superName)?.let {
+ return checkSuperClass(targetClass, it, classes)
+ }
+ // Super class not found.
+ // log.w("Class ${currentClass.superName} not found.")
+ return true
+}
+
+/**
+ * Check if a class internal name is a known legacy test base class.
+ */
+fun String.isLegacyTestBaseClass(): Boolean {
+ return this.startsWithAny(
+ "junit/framework/TestCase",
+
+ // In case the test doesn't statically include JUnit, we need
+ "android/test/AndroidTestCase",
+ "android/test/InstrumentationTestCase",
+ "android/test/InstrumentationTestSuite",
+ )
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
index 25cad02..dc934eb 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
@@ -183,7 +183,7 @@
av.visit("value", ravenwoodTestRunnerType.type)
av.visitEnd()
}
- log.d("Processed ${classInternalName.toHumanReadableClassName()}")
+ log.i("Update the @RunWith: ${classInternalName.toHumanReadableClassName()}")
}
/*
diff --git a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
new file mode 100644
index 0000000..c01fe31
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
@@ -0,0 +1,130 @@
+/*
+ * 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.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchManager.SearchContext;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.GetSchemaResponse;
+import android.app.appsearch.SetSchemaRequest;
+import android.app.appsearch.SetSchemaResponse;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class for interacting with a system server local appsearch session synchronously.
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class SyncAppSearchCallHelper implements Closeable {
+ private static final String TAG = SyncAppSearchCallHelper.class.getSimpleName();
+ private final Executor mExecutor;
+ private final AppSearchManager mAppSearchManager;
+ private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture;
+
+ public SyncAppSearchCallHelper(@NonNull AppSearchManager appSearchManager,
+ @NonNull Executor executor,
+ @NonNull SearchContext appSearchContext) {
+ Objects.requireNonNull(appSearchManager);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(appSearchContext);
+
+ mExecutor = executor;
+ mAppSearchManager = appSearchManager;
+ mSettableSessionFuture = new AndroidFuture<>();
+ mAppSearchManager.createSearchSession(
+ appSearchContext, mExecutor, mSettableSessionFuture::complete);
+ }
+
+ /**
+ * Converts a failed app search result codes into an exception.
+ */
+ @NonNull
+ private static Exception failedResultToException(@NonNull AppSearchResult appSearchResult) {
+ return switch (appSearchResult.getResultCode()) {
+ case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException(
+ appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR -> new IOException(
+ appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException(
+ appSearchResult.getErrorMessage());
+ default -> new IllegalStateException(appSearchResult.getErrorMessage());
+ };
+ }
+
+ private AppSearchSession getSession() throws Exception {
+ AppSearchResult<AppSearchSession> sessionResult = mSettableSessionFuture.get();
+ if (!sessionResult.isSuccess()) {
+ throw failedResultToException(sessionResult);
+ }
+ return sessionResult.getResultValue();
+ }
+
+ /**
+ * Gets the schema for a given app search session.
+ */
+ @WorkerThread
+ public GetSchemaResponse getSchema() throws Exception {
+ AndroidFuture<AppSearchResult<GetSchemaResponse>> settableSchemaResponse =
+ new AndroidFuture<>();
+ getSession().getSchema(mExecutor, settableSchemaResponse::complete);
+ AppSearchResult<GetSchemaResponse> schemaResponse = settableSchemaResponse.get();
+ if (schemaResponse.isSuccess()) {
+ return schemaResponse.getResultValue();
+ } else {
+ throw failedResultToException(schemaResponse);
+ }
+ }
+
+ /**
+ * Sets the schema for a given app search session.
+ */
+ @WorkerThread
+ public SetSchemaResponse setSchema(
+ @NonNull SetSchemaRequest setSchemaRequest) throws Exception {
+ AndroidFuture<AppSearchResult<SetSchemaResponse>> settableSchemaResponse =
+ new AndroidFuture<>();
+ getSession().setSchema(
+ setSchemaRequest, mExecutor, mExecutor, settableSchemaResponse::complete);
+ AppSearchResult<SetSchemaResponse> schemaResponse = settableSchemaResponse.get();
+ if (schemaResponse.isSuccess()) {
+ return schemaResponse.getResultValue();
+ } else {
+ throw failedResultToException(schemaResponse);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ getSession().close();
+ } catch (Exception ex) {
+ Slog.e(TAG, "Failed to close app search session", ex);
+ }
+ }
+}
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index 2bfdd0a..77650eb 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -28,7 +28,6 @@
],
static_libs: [
"ukey2_jni",
- "virtualdevice_flags_lib",
"virtual_camera_service_aidl-java",
],
lint: {
diff --git a/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java b/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java
index 4678a16..5fd282d 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java
@@ -190,7 +190,7 @@
}
mCachedPerUser.set(userId, cachedObservableUuids);
}
- return cachedObservableUuids;
+ return cachedObservableUuids == null ? new ArrayList<>() : cachedObservableUuids;
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
deleted file mode 100644
index 66313e6..0000000
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ /dev/null
@@ -1,17 +0,0 @@
-package {
- default_team: "trendy_team_xr_framework",
-}
-
-java_aconfig_library {
- name: "virtualdevice_flags_lib",
- aconfig_declarations: "virtualdevice_flags",
-}
-
-aconfig_declarations {
- name: "virtualdevice_flags",
- package: "com.android.server.companion.virtual",
- container: "system",
- srcs: [
- "flags.aconfig",
- ],
-}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
index b0bacfd..fed153f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
@@ -48,9 +48,6 @@
void logCreated(int deviceId, int ownerUid) {
final long token = Binder.clearCallingIdentity();
try {
- if (!Flags.dumpHistory()) {
- return;
- }
addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
} finally {
Binder.restoreCallingIdentity(token);
@@ -60,9 +57,6 @@
void logClosed(int deviceId, int ownerUid) {
final long token = Binder.clearCallingIdentity();
try {
- if (!Flags.dumpHistory()) {
- return;
- }
addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
} finally {
Binder.restoreCallingIdentity(token);
@@ -79,9 +73,6 @@
void dump(PrintWriter pw) {
final long token = Binder.clearCallingIdentity();
try {
- if (!Flags.dumpHistory()) {
- return;
- }
pw.println("VirtualDevice Log:");
UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
mContext.getPackageManager());
diff --git a/services/companion/java/com/android/server/companion/virtual/flags.aconfig b/services/companion/java/com/android/server/companion/virtual/flags.aconfig
deleted file mode 100644
index 616f5d0..0000000
--- a/services/companion/java/com/android/server/companion/virtual/flags.aconfig
+++ /dev/null
@@ -1,11 +0,0 @@
-# OLD PACKAGE, DO NOT USE: Prefer `flags.aconfig` in core/java/android/companion/virtual
-# (or other custom files) to define your flags
-package: "com.android.server.companion.virtual"
-container: "system"
-
-flag {
- name: "dump_history"
- namespace: "virtual_devices"
- description: "This flag controls if a history of virtual devices is shown in dumpsys virtualdevices"
- bug: "293114719"
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d80b38e..d121535 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16248,43 +16248,55 @@
boolean closeFd = true;
try {
- synchronized (mProcLock) {
- if (fd == null) {
- throw new IllegalArgumentException("null fd");
- }
- mBinderTransactionTrackingEnabled = false;
+ Objects.requireNonNull(fd);
- PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
- pw.println("Binder transaction traces for all processes.\n");
- mProcessList.forEachLruProcessesLOSP(true, process -> {
+ record ProcessToDump(String processName, IApplicationThread thread) { }
+
+ PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
+ pw.println("Binder transaction traces for all processes.\n");
+ final ArrayList<ProcessToDump> processes = new ArrayList<>();
+ synchronized (mProcLock) {
+ // Since dumping binder transactions is a long-running operation, we can't do it
+ // with mProcLock held. Do the initial verification here, and save the processes
+ // to dump later outside the lock.
+ final ArrayList<ProcessRecord> unverifiedProcesses =
+ new ArrayList<>(mProcessList.getLruProcessesLOSP());
+ for (int i = 0, size = unverifiedProcesses.size(); i < size; i++) {
+ ProcessRecord process = unverifiedProcesses.get(i);
final IApplicationThread thread = process.getThread();
if (!processSanityChecksLPr(process, thread)) {
- return;
+ continue;
}
-
- pw.println("Traces for process: " + process.processName);
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- thread.stopBinderTrackingAndDump(tp.getWriteFd());
- tp.go(fd.getFileDescriptor());
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println("Failure while dumping IPC traces from " + process +
- ". Exception: " + e);
- pw.flush();
- } catch (RemoteException e) {
- pw.println("Got a RemoteException while dumping IPC traces from " +
- process + ". Exception: " + e);
- pw.flush();
- }
- });
- closeFd = false;
- return true;
+ processes.add(new ProcessToDump(process.processName, process.getThread()));
+ }
+ mBinderTransactionTrackingEnabled = false;
}
+ for (int i = 0, size = processes.size(); i < size; i++) {
+ final String processName = processes.get(i).processName();
+ final IApplicationThread thread = processes.get(i).thread();
+
+ pw.println("Traces for process: " + processName);
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ thread.stopBinderTrackingAndDump(tp.getWriteFd());
+ tp.go(fd.getFileDescriptor());
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println("Failure while dumping IPC traces from " + processName +
+ ". Exception: " + e);
+ pw.flush();
+ } catch (RemoteException e) {
+ pw.println("Got a RemoteException while dumping IPC traces from " +
+ processName + ". Exception: " + e);
+ pw.flush();
+ }
+ }
+ closeFd = false;
+ return true;
} finally {
if (fd != null && closeFd) {
try {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b738482..168ec05 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -790,8 +790,7 @@
private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
private final Executor mAudioServerLifecycleExecutor;
- private final ConcurrentLinkedQueue<Future> mScheduledPermissionTasks =
- new ConcurrentLinkedQueue();
+ private final List<Future> mScheduledPermissionTasks = new ArrayList();
private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -10600,46 +10599,50 @@
// instanceof to simplify the construction requirements of AudioService for testing: no
// delayed execution during unit tests.
if (mAudioServerLifecycleExecutor instanceof ScheduledExecutorService exec) {
- // We schedule and add from a this callback thread only (serially), so the task order on
- // the serial executor matches the order on the task list. This list should almost
- // always have only two elements, except in cases of serious system contention.
- Runnable task = () -> mScheduledPermissionTasks.add(exec.schedule(() -> {
- try {
- // Clean up completed tasks before us to bound the queue length. Cancel any
- // pending permission refresh tasks, after our own, since we are about to
- // fulfill all of them. We must be the first non-completed task in the
- // queue, since the execution order matches the queue order. Note, this
- // task is the only writer on elements in the queue, and the task is
- // serialized, so
- // => no in-flight cancellation
- // => exists at least one non-completed task (ourselves)
- // => the queue is non-empty (only completed tasks removed)
- final var iter = mScheduledPermissionTasks.iterator();
- while (iter.next().isDone()) {
- iter.remove();
- }
- // iter is on the first element which is not completed (us)
- while (iter.hasNext()) {
- if (!iter.next().cancel(false)) {
- throw new AssertionError(
- "Cancel should be infallible since we" +
- "cancel from the executor");
+ // The order on the task list is an embedding on the scheduling order of the executor,
+ // since we synchronously add the scheduled task to our local queue. This list should
+ // almost always have only two elements, except in cases of serious system contention.
+ Runnable task = () -> {
+ synchronized (mScheduledPermissionTasks) {
+ mScheduledPermissionTasks.add(exec.schedule(() -> {
+ try {
+ // Our goal is to remove all tasks which don't correspond to ourselves
+ // on this queue. Either they are already done (ahead of us), or we
+ // should cancel them (behind us), since their work is redundant after
+ // we fire.
+ // We must be the first non-completed task in the queue, since the
+ // execution order matches the queue order. Note, this task is the only
+ // writer on elements in the queue, and the task is serialized, so
+ // => no in-flight cancellation
+ // => exists at least one non-completed task (ourselves)
+ // => the queue is non-empty (only completed tasks removed)
+ synchronized (mScheduledPermissionTasks) {
+ final var iter = mScheduledPermissionTasks.iterator();
+ while (iter.next().isDone()) {
+ iter.remove();
+ }
+ // iter is on the first element which is not completed (us)
+ while (iter.hasNext()) {
+ if (!iter.next().cancel(false)) {
+ throw new AssertionError(
+ "Cancel should be infallible since we" +
+ "cancel from the executor");
+ }
+ iter.remove();
+ }
}
- iter.remove();
+ mPermissionProvider.onPermissionStateChanged();
+ } catch (Exception e) {
+ // Handle executor routing exceptions to nowhere
+ Thread.getDefaultUncaughtExceptionHandler()
+ .uncaughtException(Thread.currentThread(), e);
}
- mPermissionProvider.onPermissionStateChanged();
- } catch (Exception e) {
- // Handle executor routing exceptions to nowhere
- Thread.getDefaultUncaughtExceptionHandler()
- .uncaughtException(Thread.currentThread(), e);
- }
- },
- UPDATE_DELAY_MS,
- TimeUnit.MILLISECONDS));
+ }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
+ }
+ };
mAudioSystem.listenForSystemPropertyChange(
PermissionManager.CACHE_KEY_PACKAGE_INFO,
task);
- task.run();
} else {
mAudioSystem.listenForSystemPropertyChange(
PermissionManager.CACHE_KEY_PACKAGE_INFO,
@@ -14710,7 +14713,11 @@
@Override
/** @see AudioManager#permissionUpdateBarrier() */
public void permissionUpdateBarrier() {
- for (var x : List.copyOf(mScheduledPermissionTasks)) {
+ List<Future> snapshot;
+ synchronized (mScheduledPermissionTasks) {
+ snapshot = List.copyOf(mScheduledPermissionTasks);
+ }
+ for (var x : snapshot) {
try {
x.get();
} catch (CancellationException e) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index bbe7b2b..3c7b9d3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4328,6 +4328,7 @@
HdmiCecLocalDevicePlayback playback = playback();
HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
if (playback != null) {
+ playback.dismissUiOnActiveSourceStatusRecovered();
playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
caller);
playback.wakeUpIfActiveSource();
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index e3061a7..4135161 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -69,6 +69,7 @@
mUserManager = mSystemUserContext.getSystemService(UserManager.class);
NotificationChannel channel = new NotificationChannel(BUSN_CHANNEL_ID, BUSN_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH);
+ channel.setSound(null, null);
mNotificationManager.createNotificationChannel(channel);
setupFocusControlAudioPolicy();
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 95e5b84..2bc6d53 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -503,26 +503,21 @@
String restriction,
boolean isMainUser,
boolean isProfileOwnerOnOrgOwnedDevice) {
- if (android.app.admin.flags.Flags.esimManagementEnabled()) {
- if (IMMUTABLE_BY_OWNERS.contains(restriction)) {
- return false;
- }
- if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)) {
- return false;
- }
- if (!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)) {
- return false;
- }
- if (!isProfileOwnerOnOrgOwnedDevice
- && PROFILE_OWNER_ORGANIZATION_OWNED_PROFILE_RESTRICTIONS.contains(
- restriction)) {
- return false;
- }
- return true;
+ if (IMMUTABLE_BY_OWNERS.contains(restriction)) {
+ return false;
}
- return !IMMUTABLE_BY_OWNERS.contains(restriction)
- && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)
- && !(!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction));
+ if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)) {
+ return false;
+ }
+ if (!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)) {
+ return false;
+ }
+ if (!isProfileOwnerOnOrgOwnedDevice
+ && PROFILE_OWNER_ORGANIZATION_OWNED_PROFILE_RESTRICTIONS.contains(
+ restriction)) {
+ return false;
+ }
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
index 440d251..eb5361c 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
@@ -26,6 +26,11 @@
* @hide
*/
public class CasResource {
+ /**
+ * Handle of the current resource. Should not be changed and should be aligned with the driver
+ * level implementation.
+ */
+ final int mHandle;
private final int mSystemId;
@@ -39,11 +44,16 @@
private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>();
CasResource(Builder builder) {
+ this.mHandle = builder.mHandle;
this.mSystemId = builder.mSystemId;
this.mMaxSessionNum = builder.mMaxSessionNum;
this.mAvailableSessionNum = builder.mMaxSessionNum;
}
+ public int getHandle() {
+ return mHandle;
+ }
+
public int getSystemId() {
return mSystemId;
}
@@ -136,10 +146,12 @@
*/
public static class Builder {
+ private final int mHandle;
private int mSystemId;
protected int mMaxSessionNum;
- Builder(int systemId) {
+ Builder(int handle, int systemId) {
+ this.mHandle = handle;
this.mSystemId = systemId;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
index 31149f3..5cef729 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
@@ -42,8 +42,8 @@
* Builder class for {@link CiCamResource}.
*/
public static class Builder extends CasResource.Builder {
- Builder(int systemId) {
- super(systemId);
+ Builder(int handle, int systemId) {
+ super(handle, systemId);
}
/**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 0afb049..9229f7f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -203,13 +203,7 @@
@Override
public void unregisterClientProfile(int clientId) throws RemoteException {
enforceTrmAccessPermission("unregisterClientProfile");
- synchronized (mLock) {
- if (!checkClientExists(clientId)) {
- Slog.e(TAG, "Unregistering non exists client:" + clientId);
- return;
- }
- unregisterClientProfileInternal(clientId);
- }
+ unregisterClientProfileInternal(clientId);
}
@Override
@@ -291,20 +285,7 @@
Slog.e(TAG, "frontendHandle can't be null");
return false;
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- Slog.e(TAG, "Request frontend from unregistered client: "
- + request.clientId);
- return false;
- }
- // If the request client is holding or sharing a frontend, throw an exception.
- if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
- Slog.e(TAG, "Release frontend before requesting another one. Client id: "
- + request.clientId);
- return false;
- }
- return requestFrontendInternal(request, frontendHandle);
- }
+ return requestFrontendInternal(request, frontendHandle);
}
@Override
@@ -376,13 +357,7 @@
if (demuxHandle == null) {
throw new RemoteException("demuxHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request demux from unregistered client:"
- + request.clientId);
- }
- return requestDemuxInternal(request, demuxHandle);
- }
+ return requestDemuxInternal(request, demuxHandle);
}
@Override
@@ -409,13 +384,7 @@
if (casSessionHandle == null) {
throw new RemoteException("casSessionHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request cas from unregistered client:"
- + request.clientId);
- }
- return requestCasSessionInternal(request, casSessionHandle);
- }
+ return requestCasSessionInternal(request, casSessionHandle);
}
@Override
@@ -425,13 +394,7 @@
if (ciCamHandle == null) {
throw new RemoteException("ciCamHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request ciCam from unregistered client:"
- + request.clientId);
- }
- return requestCiCamInternal(request, ciCamHandle);
- }
+ return requestCiCamInternal(request, ciCamHandle);
}
@Override
@@ -442,42 +405,14 @@
if (lnbHandle == null) {
throw new RemoteException("lnbHandle can't be null");
}
- synchronized (mLock) {
- if (!checkClientExists(request.clientId)) {
- throw new RemoteException("Request lnb from unregistered client:"
- + request.clientId);
- }
- return requestLnbInternal(request, lnbHandle);
- }
+ return requestLnbInternal(request, lnbHandle);
}
@Override
public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException {
enforceTunerAccessPermission("releaseFrontend");
enforceTrmAccessPermission("releaseFrontend");
- if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
- frontendHandle)) {
- throw new RemoteException("frontendHandle can't be invalid");
- }
- synchronized (mLock) {
- if (!checkClientExists(clientId)) {
- throw new RemoteException("Release frontend from unregistered client:"
- + clientId);
- }
- FrontendResource fe = getFrontendResource(frontendHandle);
- if (fe == null) {
- throw new RemoteException("Releasing frontend does not exist.");
- }
- int ownerClientId = fe.getOwnerClientId();
- ClientProfile ownerProfile = getClientProfile(ownerClientId);
- if (ownerClientId != clientId
- && (ownerProfile != null
- && !ownerProfile.getShareFeClientIds().contains(clientId))) {
- throw new RemoteException(
- "Client is not the current owner of the releasing fe.");
- }
- releaseFrontendInternal(fe, clientId);
- }
+ releaseFrontendInternal(frontendHandle, clientId);
}
@Override
@@ -746,17 +681,23 @@
@VisibleForTesting
protected void unregisterClientProfileInternal(int clientId) {
- if (DEBUG) {
- Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
- }
- removeClientProfile(clientId);
- // Remove the Media Resource Manager callingPid to tvAppId mapping
- if (mMediaResourceManager != null) {
- try {
- mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
- + " remote exception: " + e);
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ Slog.e(TAG, "Unregistering non exists client:" + clientId);
+ return;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+ }
+ removeClientProfile(clientId);
+ // Remove the Media Resource Manager callingPid to tvAppId mapping
+ if (mMediaResourceManager != null) {
+ try {
+ mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
+ + " remote exception: " + e);
+ }
}
}
}
@@ -992,10 +933,14 @@
return;
}
// Add the new Cas Resource.
- cas = new CasResource.Builder(casSystemId)
+ int casSessionHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSystemId);
+ cas = new CasResource.Builder(casSessionHandle, casSystemId)
.maxSessionNum(maxSessionNum)
.build();
- ciCam = new CiCamResource.Builder(casSystemId)
+ int ciCamHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, casSystemId);
+ ciCam = new CiCamResource.Builder(ciCamHandle, casSystemId)
.maxSessionNum(maxSessionNum)
.build();
addCasResource(cas);
@@ -1007,86 +952,120 @@
if (DEBUG) {
Slog.d(TAG, "requestFrontend(request=" + request + ")");
}
-
- frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- // TODO: check if this is really needed
- if (requestClient == null) {
+ int[] reclaimOwnerId = new int[1];
+ if (!claimFrontend(request, frontendHandle, reclaimOwnerId)) {
return false;
}
- clientPriorityUpdateOnRequest(requestClient);
- int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- // If the desired frontend id was specified, we only need to check the frontend.
- boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest.DEFAULT_DESIRED_ID;
- for (FrontendResource fr : getFrontendResources().values()) {
- int frontendId = getResourceIdFromHandle(fr.getHandle());
- if (fr.getType() == request.frontendType
- && (!hasDesiredFrontend || frontendId == request.desiredId)) {
- if (!fr.isInUse()) {
- // Unused resource cannot be acquired if the max is already reached, but
- // TRM still has to look for the reclaim candidate
- if (isFrontendMaxNumUseReached(request.frontendType)) {
- continue;
- }
- // Grant unused frontend with no exclusive group members first.
- if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
- grantingFrontendHandle = fr.getHandle();
- break;
- } else if (grantingFrontendHandle
- == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- // Grant the unused frontend with lower id first if all the unused
- // frontends have exclusive group members.
- grantingFrontendHandle = fr.getHandle();
- }
- } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- // Record the frontend id with the lowest client priority among all the
- // in use frontends when no available frontend has been found.
- int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
- if (currentLowestPriority > priority) {
- // we need to check the max used num if the target frontend type is not
- // currently in primary use (and simply blocked due to exclusive group)
- ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId());
- int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
- FrontendResource primaryFe = getFrontendResource(primaryFeId);
- if (fr.getType() != primaryFe.getType()
- && isFrontendMaxNumUseReached(fr.getType())) {
+ if (frontendHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
+ }
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0], TunerResourceManager
+ .TUNER_RESOURCE_TYPE_FRONTEND)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (getFrontendResource(frontendHandle[0]).isInUse()) {
+ Slog.e(TAG, "Reclaimed frontend still in use");
+ return false;
+ }
+ updateFrontendClientMappingOnNewGrant(frontendHandle[0], request.clientId);
+ }
+ }
+ return true;
+ }
+
+ protected boolean claimFrontend(
+ TunerFrontendRequest request,
+ int[] frontendHandle,
+ int[] reclaimOwnerId
+ ) {
+ frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ Slog.e(TAG, "Request frontend from unregistered client: "
+ + request.clientId);
+ return false;
+ }
+ // If the request client is holding or sharing a frontend, throw an exception.
+ if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
+ Slog.e(TAG, "Release frontend before requesting another one. Client id: "
+ + request.clientId);
+ return false;
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ FrontendResource grantingFrontend = null;
+ FrontendResource inUseLowestPriorityFrontend = null;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ // If the desired frontend id was specified, we only need to check the frontend.
+ boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest
+ .DEFAULT_DESIRED_ID;
+ for (FrontendResource fr : getFrontendResources().values()) {
+ int frontendId = getResourceIdFromHandle(fr.getHandle());
+ if (fr.getType() == request.frontendType
+ && (!hasDesiredFrontend || frontendId == request.desiredId)) {
+ if (!fr.isInUse()) {
+ // Unused resource cannot be acquired if the max is already reached, but
+ // TRM still has to look for the reclaim candidate
+ if (isFrontendMaxNumUseReached(request.frontendType)) {
continue;
}
- // update the target frontend
- inUseLowestPriorityFrHandle = fr.getHandle();
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(fr.getOwnerClientId())).getProcessId());
+ // Grant unused frontend with no exclusive group members first.
+ if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
+ grantingFrontend = fr;
+ break;
+ } else if (grantingFrontend == null) {
+ // Grant the unused frontend with lower id first if all the unused
+ // frontends have exclusive group members.
+ grantingFrontend = fr;
+ }
+ } else if (grantingFrontend == null) {
+ // Record the frontend id with the lowest client priority among all the
+ // in use frontends when no available frontend has been found.
+ int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
+ if (currentLowestPriority > priority) {
+ // we need to check the max used num if the target frontend type is not
+ // currently in primary use (and simply blocked due to exclusive group)
+ ClientProfile targetOwnerProfile =
+ getClientProfile(fr.getOwnerClientId());
+ int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
+ FrontendResource primaryFe = getFrontendResource(primaryFeId);
+ if (fr.getType() != primaryFe.getType()
+ && isFrontendMaxNumUseReached(fr.getType())) {
+ continue;
+ }
+ // update the target frontend
+ inUseLowestPriorityFrontend = fr;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(fr.getOwnerClientId())).getProcessId());
+ }
}
}
}
- }
- // Grant frontend when there is unused resource.
- if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- frontendHandle[0] = grantingFrontendHandle;
- updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId);
- return true;
- }
-
- // When all the resources are occupied, grant the lowest priority resource if the
- // request client has higher priority.
- if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
- && ((requestClient.getPriority() > currentLowestPriority) || (
- (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(
- getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(),
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
- return false;
+ // Grant frontend when there is unused resource.
+ if (grantingFrontend != null) {
+ updateFrontendClientMappingOnNewGrant(grantingFrontend.getHandle(),
+ request.clientId);
+ frontendHandle[0] = grantingFrontend.getHandle();
+ return true;
}
- frontendHandle[0] = inUseLowestPriorityFrHandle;
- updateFrontendClientMappingOnNewGrant(
- inUseLowestPriorityFrHandle, request.clientId);
- return true;
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityFrontend != null
+ && ((requestClient.getPriority() > currentLowestPriority)
+ || ((requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ frontendHandle[0] = inUseLowestPriorityFrontend.getHandle();
+ reclaimOwnerId[0] = inUseLowestPriorityFrontend.getOwnerClientId();
+ return true;
+ }
}
return false;
@@ -1192,165 +1171,257 @@
}
@VisibleForTesting
- protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
+ protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)
+ throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestLnb(request=" + request + ")");
}
-
- lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- clientPriorityUpdateOnRequest(requestClient);
- int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- for (LnbResource lnb : getLnbResources().values()) {
- if (!lnb.isInUse()) {
- // Grant the unused lnb with lower handle first
- grantingLnbHandle = lnb.getHandle();
- break;
- } else {
- // Record the lnb id with the lowest client priority among all the
- // in use lnb when no available lnb has been found.
- int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
- if (currentLowestPriority > priority) {
- inUseLowestPriorityLnbHandle = lnb.getHandle();
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(lnb.getOwnerClientId())).getProcessId());
- }
- }
+ int[] reclaimOwnerId = new int[1];
+ if (!claimLnb(request, lnbHandle, reclaimOwnerId)) {
+ return false;
}
-
- // Grant Lnb when there is unused resource.
- if (grantingLnbHandle > -1) {
- lnbHandle[0] = grantingLnbHandle;
- updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId);
- return true;
+ if (lnbHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
}
-
- // When all the resources are occupied, grant the lowest priority resource if the
- // request client has higher priority.
- if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE
- && ((requestClient.getPriority() > currentLowestPriority) || (
- (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(),
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) {
return false;
}
- lnbHandle[0] = inUseLowestPriorityLnbHandle;
- updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId);
- return true;
+ synchronized (mLock) {
+ if (getLnbResource(lnbHandle[0]).isInUse()) {
+ Slog.e(TAG, "Reclaimed lnb still in use");
+ return false;
+ }
+ updateLnbClientMappingOnNewGrant(lnbHandle[0], request.clientId);
+ }
+ }
+ return true;
+ }
+
+ protected boolean claimLnb(TunerLnbRequest request, int[] lnbHandle, int[] reclaimOwnerId)
+ throws RemoteException {
+ lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request lnb from unregistered client:"
+ + request.clientId);
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ LnbResource grantingLnb = null;
+ LnbResource inUseLowestPriorityLnb = null;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ for (LnbResource lnb : getLnbResources().values()) {
+ if (!lnb.isInUse()) {
+ // Grant the unused lnb with lower handle first
+ grantingLnb = lnb;
+ break;
+ } else {
+ // Record the lnb id with the lowest client priority among all the
+ // in use lnb when no available lnb has been found.
+ int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
+ if (currentLowestPriority > priority) {
+ inUseLowestPriorityLnb = lnb;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(lnb.getOwnerClientId())).getProcessId());
+ }
+ }
+ }
+
+ // Grant Lnb when there is unused resource.
+ if (grantingLnb != null) {
+ updateLnbClientMappingOnNewGrant(grantingLnb.getHandle(), request.clientId);
+ lnbHandle[0] = grantingLnb.getHandle();
+ return true;
+ }
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityLnb != null
+ && ((requestClient.getPriority() > currentLowestPriority) || (
+ (requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ lnbHandle[0] = inUseLowestPriorityLnb.getHandle();
+ reclaimOwnerId[0] = inUseLowestPriorityLnb.getOwnerClientId();
+ return true;
+ }
}
return false;
}
@VisibleForTesting
- protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) {
+ protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle)
+ throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestCasSession(request=" + request + ")");
}
- CasResource cas = getCasResource(request.casSystemId);
- // Unregistered Cas System is treated as having unlimited sessions.
- if (cas == null) {
- cas = new CasResource.Builder(request.casSystemId)
- .maxSessionNum(Integer.MAX_VALUE)
- .build();
- addCasResource(cas);
+ int[] reclaimOwnerId = new int[1];
+ if (!claimCasSession(request, casSessionHandle, reclaimOwnerId)) {
+ return false;
}
- casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- clientPriorityUpdateOnRequest(requestClient);
- int lowestPriorityOwnerId = -1;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- if (!cas.isFullyUsed()) {
- casSessionHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
- updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
- return true;
+ if (casSessionHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
}
- for (int ownerId : cas.getOwnerClientIds()) {
- // Record the client id with lowest priority that is using the current Cas system.
- int priority = updateAndGetOwnerClientPriority(ownerId);
- if (currentLowestPriority > priority) {
- lowestPriorityOwnerId = ownerId;
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(ownerId)).getProcessId());
- }
- }
-
- // When all the Cas sessions are occupied, reclaim the lowest priority client if the
- // request client has higher priority.
- if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority)
- || ((requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(lowestPriorityOwnerId,
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) {
return false;
}
- casSessionHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
- updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
- return true;
+ synchronized (mLock) {
+ if (getCasResource(request.casSystemId).isFullyUsed()) {
+ Slog.e(TAG, "Reclaimed cas still fully used");
+ return false;
+ }
+ updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+ }
}
+ return true;
+ }
+
+ protected boolean claimCasSession(CasSessionRequest request, int[] casSessionHandle,
+ int[] reclaimOwnerId) throws RemoteException {
+ casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request cas from unregistered client:"
+ + request.clientId);
+ }
+ CasResource cas = getCasResource(request.casSystemId);
+ // Unregistered Cas System is treated as having unlimited sessions.
+ if (cas == null) {
+ int resourceHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, request.clientId);
+ cas = new CasResource.Builder(resourceHandle, request.casSystemId)
+ .maxSessionNum(Integer.MAX_VALUE)
+ .build();
+ addCasResource(cas);
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ int lowestPriorityOwnerId = INVALID_CLIENT_ID;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ if (!cas.isFullyUsed()) {
+ updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+ casSessionHandle[0] = cas.getHandle();
+ return true;
+ }
+ for (int ownerId : cas.getOwnerClientIds()) {
+ // Record the client id with lowest priority that is using the current Cas system.
+ int priority = updateAndGetOwnerClientPriority(ownerId);
+ if (currentLowestPriority > priority) {
+ lowestPriorityOwnerId = ownerId;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(ownerId)).getProcessId());
+ }
+ }
+
+ // When all the Cas sessions are occupied, reclaim the lowest priority client if the
+ // request client has higher priority.
+ if (lowestPriorityOwnerId != INVALID_CLIENT_ID
+ && ((requestClient.getPriority() > currentLowestPriority)
+ || ((requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ casSessionHandle[0] = cas.getHandle();
+ reclaimOwnerId[0] = lowestPriorityOwnerId;
+ return true;
+ }
+ }
+
return false;
}
@VisibleForTesting
- protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) {
+ protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle)
+ throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")");
}
- CiCamResource ciCam = getCiCamResource(request.ciCamId);
- // Unregistered Cas System is treated as having unlimited sessions.
- if (ciCam == null) {
- ciCam = new CiCamResource.Builder(request.ciCamId)
- .maxSessionNum(Integer.MAX_VALUE)
- .build();
- addCiCamResource(ciCam);
+ int[] reclaimOwnerId = new int[1];
+ if (!claimCiCam(request, ciCamHandle, reclaimOwnerId)) {
+ return false;
}
- ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
- clientPriorityUpdateOnRequest(requestClient);
- int lowestPriorityOwnerId = -1;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- if (!ciCam.isFullyUsed()) {
- ciCamHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
- updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
- return true;
+ if (ciCamHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
}
- for (int ownerId : ciCam.getOwnerClientIds()) {
- // Record the client id with lowest priority that is using the current Cas system.
- int priority = updateAndGetOwnerClientPriority(ownerId);
- if (currentLowestPriority > priority) {
- lowestPriorityOwnerId = ownerId;
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(ownerId)).getProcessId());
- }
- }
-
- // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
- // request client has higher priority.
- if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority)
- || ((requestClient.getPriority() == currentLowestPriority)
- && isRequestFromSameProcess))) {
- if (!reclaimResource(lowestPriorityOwnerId,
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) {
return false;
}
- ciCamHandle[0] = generateResourceHandle(
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
- updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
- return true;
+ synchronized (mLock) {
+ if (getCiCamResource(request.ciCamId).isFullyUsed()) {
+ Slog.e(TAG, "Reclaimed ciCam still fully used");
+ return false;
+ }
+ updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ }
}
+ return true;
+ }
+
+ protected boolean claimCiCam(TunerCiCamRequest request, int[] ciCamHandle,
+ int[] reclaimOwnerId) throws RemoteException {
+ ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request ciCam from unregistered client:"
+ + request.clientId);
+ }
+ CiCamResource ciCam = getCiCamResource(request.ciCamId);
+ // Unregistered CiCam is treated as having unlimited sessions.
+ if (ciCam == null) {
+ int resourceHandle = generateResourceHandle(
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, request.ciCamId);
+ ciCam = new CiCamResource.Builder(resourceHandle, request.ciCamId)
+ .maxSessionNum(Integer.MAX_VALUE)
+ .build();
+ addCiCamResource(ciCam);
+ }
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ clientPriorityUpdateOnRequest(requestClient);
+ int lowestPriorityOwnerId = INVALID_CLIENT_ID;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ if (!ciCam.isFullyUsed()) {
+ updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+ ciCamHandle[0] = ciCam.getHandle();
+ return true;
+ }
+ for (int ownerId : ciCam.getOwnerClientIds()) {
+ // Record the client id with lowest priority that is using the current CiCam.
+ int priority = updateAndGetOwnerClientPriority(ownerId);
+ if (currentLowestPriority > priority) {
+ lowestPriorityOwnerId = ownerId;
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(ownerId)).getProcessId());
+ }
+ }
+
+ // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
+ // request client has higher priority.
+ if (lowestPriorityOwnerId != INVALID_CLIENT_ID
+ && ((requestClient.getPriority() > currentLowestPriority)
+ || ((requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ ciCamHandle[0] = ciCam.getHandle();
+ reclaimOwnerId[0] = lowestPriorityOwnerId;
+ return true;
+ }
+ }
+
return false;
}
@@ -1383,20 +1454,49 @@
}
@VisibleForTesting
- protected void releaseFrontendInternal(FrontendResource fe, int clientId) {
+ protected void releaseFrontendInternal(int frontendHandle, int clientId)
+ throws RemoteException {
if (DEBUG) {
- Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )");
+ Slog.d(TAG, "releaseFrontend(id=" + frontendHandle + ", clientId=" + clientId + " )");
}
- if (clientId == fe.getOwnerClientId()) {
- ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
- if (ownerClient != null) {
- for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
- reclaimResource(shareOwnerId,
- TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+ if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
+ frontendHandle)) {
+ throw new RemoteException("frontendHandle can't be invalid");
+ }
+ Set<Integer> reclaimedResourceOwnerIds = unclaimFrontend(frontendHandle, clientId);
+ if (reclaimedResourceOwnerIds != null) {
+ for (int shareOwnerId : reclaimedResourceOwnerIds) {
+ reclaimResource(shareOwnerId,
+ TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+ }
+ }
+ synchronized (mLock) {
+ clearFrontendAndClientMapping(getClientProfile(clientId));
+ }
+ }
+
+ private Set<Integer> unclaimFrontend(int frontendHandle, int clientId) throws RemoteException {
+ Set<Integer> reclaimedResourceOwnerIds = null;
+ synchronized (mLock) {
+ if (!checkClientExists(clientId)) {
+ throw new RemoteException("Release frontend from unregistered client:"
+ + clientId);
+ }
+ FrontendResource fe = getFrontendResource(frontendHandle);
+ if (fe == null) {
+ throw new RemoteException("Releasing frontend does not exist.");
+ }
+ int ownerClientId = fe.getOwnerClientId();
+ ClientProfile ownerProfile = getClientProfile(ownerClientId);
+ if (ownerClientId == clientId) {
+ reclaimedResourceOwnerIds = ownerProfile.getShareFeClientIds();
+ } else {
+ if (!ownerProfile.getShareFeClientIds().contains(clientId)) {
+ throw new RemoteException("Client is not a sharee of the releasing fe.");
}
}
}
- clearFrontendAndClientMapping(getClientProfile(clientId));
+ return reclaimedResourceOwnerIds;
}
@VisibleForTesting
@@ -1432,103 +1532,129 @@
}
@VisibleForTesting
- protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+ public boolean requestDemuxInternal(@NonNull TunerDemuxRequest request,
+ @NonNull int[] demuxHandle) throws RemoteException {
if (DEBUG) {
Slog.d(TAG, "requestDemux(request=" + request + ")");
}
-
- // For Tuner 2.0 and below or any HW constraint devices that are unable to support
- // ITuner.openDemuxById(), demux resources are not really managed under TRM and
- // mDemuxResources.size() will be zero
- if (mDemuxResources.size() == 0) {
- // There are enough Demux resources, so we don't manage Demux in R.
- demuxHandle[0] =
- generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
- return true;
- }
-
- demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- ClientProfile requestClient = getClientProfile(request.clientId);
-
- if (requestClient == null) {
+ int[] reclaimOwnerId = new int[1];
+ if (!claimDemux(request, demuxHandle, reclaimOwnerId)) {
return false;
}
+ if (demuxHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+ return false;
+ }
+ if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+ if (!reclaimResource(reclaimOwnerId[0],
+ TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (getDemuxResource(demuxHandle[0]).isInUse()) {
+ Slog.e(TAG, "Reclaimed demux still in use");
+ return false;
+ }
+ updateDemuxClientMappingOnNewGrant(demuxHandle[0], request.clientId);
+ }
+ }
+ return true;
+ }
- clientPriorityUpdateOnRequest(requestClient);
- int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
- // Priority max value is 1000
- int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
- boolean isRequestFromSameProcess = false;
- // If the desired demux id was specified, we only need to check the demux.
- boolean hasDesiredDemuxCap = request.desiredFilterTypes
- != DemuxFilterMainType.UNDEFINED;
- int smallestNumOfSupportedCaps = Integer.SIZE + 1;
- int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
- for (DemuxResource dr : getDemuxResources().values()) {
- if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
- if (!dr.isInUse()) {
- int numOfSupportedCaps = dr.getNumOfCaps();
+ protected boolean claimDemux(TunerDemuxRequest request, int[] demuxHandle, int[] reclaimOwnerId)
+ throws RemoteException {
+ demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+ reclaimOwnerId[0] = INVALID_CLIENT_ID;
+ synchronized (mLock) {
+ if (!checkClientExists(request.clientId)) {
+ throw new RemoteException("Request demux from unregistered client:"
+ + request.clientId);
+ }
- // look for the demux with minimum caps supporting the desired caps
- if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
- smallestNumOfSupportedCaps = numOfSupportedCaps;
- grantingDemuxHandle = dr.getHandle();
- }
- } else if (grantingDemuxHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- // Record the demux id with the lowest client priority among all the
- // in use demuxes when no availabledemux has been found.
- int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
- if (currentLowestPriority >= priority) {
+ // For Tuner 2.0 and below or any HW constraint devices that are unable to support
+ // ITuner.openDemuxById(), demux resources are not really managed under TRM and
+ // mDemuxResources.size() will be zero
+ if (mDemuxResources.size() == 0) {
+ // There are enough Demux resources, so we don't manage Demux in R.
+ demuxHandle[0] =
+ generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
+ return true;
+ }
+
+ ClientProfile requestClient = getClientProfile(request.clientId);
+ if (requestClient == null) {
+ return false;
+ }
+ clientPriorityUpdateOnRequest(requestClient);
+ DemuxResource grantingDemux = null;
+ DemuxResource inUseLowestPriorityDemux = null;
+ // Priority max value is 1000
+ int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+ boolean isRequestFromSameProcess = false;
+ // If the desired demux id was specified, we only need to check the demux.
+ boolean hasDesiredDemuxCap = request.desiredFilterTypes
+ != DemuxFilterMainType.UNDEFINED;
+ int smallestNumOfSupportedCaps = Integer.SIZE + 1;
+ int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
+ for (DemuxResource dr : getDemuxResources().values()) {
+ if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
+ if (!dr.isInUse()) {
int numOfSupportedCaps = dr.getNumOfCaps();
- boolean shouldUpdate = false;
- // update lowest priority
- if (currentLowestPriority > priority) {
- currentLowestPriority = priority;
- isRequestFromSameProcess = (requestClient.getProcessId()
- == (getClientProfile(dr.getOwnerClientId())).getProcessId());
- // reset the smallest caps when lower priority resource is found
- smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
-
- shouldUpdate = true;
- } else {
- // This is the case when the priority is the same as previously found
- // one. Update smallest caps when priority.
- if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
- smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
- shouldUpdate = true;
- }
+ // look for the demux with minimum caps supporting the desired caps
+ if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
+ smallestNumOfSupportedCaps = numOfSupportedCaps;
+ grantingDemux = dr;
}
- if (shouldUpdate) {
- inUseLowestPriorityDrHandle = dr.getHandle();
+ } else if (grantingDemux == null) {
+ // Record the demux id with the lowest client priority among all the
+ // in use demuxes when no availabledemux has been found.
+ int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
+ if (currentLowestPriority >= priority) {
+ int numOfSupportedCaps = dr.getNumOfCaps();
+ boolean shouldUpdate = false;
+ // update lowest priority
+ if (currentLowestPriority > priority) {
+ currentLowestPriority = priority;
+ isRequestFromSameProcess = (requestClient.getProcessId()
+ == (getClientProfile(dr.getOwnerClientId())).getProcessId());
+
+ // reset the smallest caps when lower priority resource is found
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+
+ shouldUpdate = true;
+ } else {
+ // This is the case when the priority is the same as previously
+ // found one. Update smallest caps when priority.
+ if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
+ smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+ shouldUpdate = true;
+ }
+ }
+ if (shouldUpdate) {
+ inUseLowestPriorityDemux = dr;
+ }
}
}
}
}
- }
- // Grant demux when there is unused resource.
- if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
- demuxHandle[0] = grantingDemuxHandle;
- updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId);
- return true;
- }
-
- // When all the resources are occupied, grant the lowest priority resource if the
- // request client has higher priority.
- if (inUseLowestPriorityDrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
- && ((requestClient.getPriority() > currentLowestPriority) || (
- (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
- if (!reclaimResource(
- getDemuxResource(inUseLowestPriorityDrHandle).getOwnerClientId(),
- TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
- return false;
+ // Grant demux when there is unused resource.
+ if (grantingDemux != null) {
+ updateDemuxClientMappingOnNewGrant(grantingDemux.getHandle(), request.clientId);
+ demuxHandle[0] = grantingDemux.getHandle();
+ return true;
}
- demuxHandle[0] = inUseLowestPriorityDrHandle;
- updateDemuxClientMappingOnNewGrant(
- inUseLowestPriorityDrHandle, request.clientId);
- return true;
+
+ // When all the resources are occupied, grant the lowest priority resource if the
+ // request client has higher priority.
+ if (inUseLowestPriorityDemux != null
+ && ((requestClient.getPriority() > currentLowestPriority) || (
+ (requestClient.getPriority() == currentLowestPriority)
+ && isRequestFromSameProcess))) {
+ demuxHandle[0] = inUseLowestPriorityDemux.getHandle();
+ reclaimOwnerId[0] = inUseLowestPriorityDemux.getOwnerClientId();
+ return true;
+ }
}
return false;
@@ -1792,7 +1918,9 @@
return;
}
- mListeners.put(clientId, record);
+ synchronized (mLock) {
+ mListeners.put(clientId, record);
+ }
}
@VisibleForTesting
@@ -1808,33 +1936,44 @@
// Reclaim all the resources of the share owners of the frontend that is used by the current
// resource reclaimed client.
- ClientProfile profile = getClientProfile(reclaimingClientId);
- // TODO: check if this check is really needed.
- if (profile == null) {
- return true;
- }
- Set<Integer> shareFeClientIds = profile.getShareFeClientIds();
- for (int clientId : shareFeClientIds) {
- try {
- mListeners.get(clientId).getListener().onReclaimResources();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
- return false;
+ Set<Integer> shareFeClientIds;
+ synchronized (mLock) {
+ ClientProfile profile = getClientProfile(reclaimingClientId);
+ if (profile == null) {
+ return true;
}
- clearAllResourcesAndClientMapping(getClientProfile(clientId));
+ shareFeClientIds = profile.getShareFeClientIds();
+ }
+ ResourcesReclaimListenerRecord listenerRecord = null;
+ for (int clientId : shareFeClientIds) {
+ synchronized (mLock) {
+ listenerRecord = mListeners.get(clientId);
+ }
+ if (listenerRecord != null) {
+ try {
+ listenerRecord.getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
+ }
+ }
}
if (DEBUG) {
Slog.d(TAG, "Reclaiming resources because higher priority client request resource type "
+ resourceType + ", clientId:" + reclaimingClientId);
}
- try {
- mListeners.get(reclaimingClientId).getListener().onReclaimResources();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
- return false;
+
+ synchronized (mLock) {
+ listenerRecord = mListeners.get(reclaimingClientId);
}
- clearAllResourcesAndClientMapping(profile);
+ if (listenerRecord != null) {
+ try {
+ listenerRecord.getListener().onReclaimResources();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
+ }
+ }
+
return true;
}
@@ -2258,6 +2397,7 @@
addResourcesReclaimListener(clientId, listener);
}
+ @SuppressWarnings("GuardedBy") // Lock is held on mListeners
private void removeClientProfile(int clientId) {
for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) {
clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
@@ -2265,12 +2405,9 @@
clearAllResourcesAndClientMapping(getClientProfile(clientId));
mClientProfiles.remove(clientId);
- // it may be called by unregisterClientProfileInternal under test
- synchronized (mLock) {
- ResourcesReclaimListenerRecord record = mListeners.remove(clientId);
- if (record != null) {
- record.getListener().asBinder().unlinkToDeath(record, 0);
- }
+ ResourcesReclaimListenerRecord record = mListeners.remove(clientId);
+ if (record != null) {
+ record.getListener().asBinder().unlinkToDeath(record, 0);
}
}
@@ -2304,7 +2441,8 @@
profile.releaseFrontend();
}
- private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+ @VisibleForTesting
+ protected void clearAllResourcesAndClientMapping(ClientProfile profile) {
// TODO: check if this check is really needed. Maybe needed for reclaimResource path.
if (profile == null) {
return;
diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
new file mode 100644
index 0000000..b4d3862
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
@@ -0,0 +1,146 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
+import android.os.IBinder;
+import android.os.VibrationAttributes;
+import android.os.vibrator.Flags;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * A vibration session holding a single {@link ExternalVibration} request.
+ */
+final class ExternalVibrationSession extends Vibration
+ implements VibrationSession, IBinder.DeathRecipient {
+
+ private final ExternalVibration mExternalVibration;
+ private final ExternalVibrationScale mScale = new ExternalVibrationScale();
+
+ @Nullable
+ private Runnable mBinderDeathCallback;
+
+ ExternalVibrationSession(ExternalVibration externalVibration) {
+ super(externalVibration.getToken(), new CallerInfo(
+ externalVibration.getVibrationAttributes(), externalVibration.getUid(),
+ // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
+ // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
+ Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
+ mExternalVibration = externalVibration;
+ }
+
+ public ExternalVibrationScale getScale() {
+ return mScale;
+ }
+
+ @Override
+ public CallerInfo getCallerInfo() {
+ return callerInfo;
+ }
+
+ @Override
+ public VibrationSession.DebugInfo getDebugInfo() {
+ return new Vibration.DebugInfoImpl(getStatus(), stats, /* playedEffect= */ null,
+ /* originalEffect= */ null, mScale.scaleLevel, mScale.adaptiveHapticsScale,
+ callerInfo);
+ }
+
+ @Override
+ public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
+ return new VibrationStats.StatsInfo(
+ mExternalVibration.getUid(),
+ FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
+ mExternalVibration.getVibrationAttributes().getUsage(), getStatus(), stats,
+ completionUptimeMillis);
+ }
+
+ @Override
+ public boolean isRepeating() {
+ // We don't currently know if the external vibration is repeating, so we just use a
+ // heuristic based on the usage. Ideally this would be propagated in the ExternalVibration.
+ int usage = mExternalVibration.getVibrationAttributes().getUsage();
+ return usage == VibrationAttributes.USAGE_RINGTONE
+ || usage == VibrationAttributes.USAGE_ALARM;
+ }
+
+ @Override
+ public void linkToDeath(Runnable callback) {
+ synchronized (this) {
+ mBinderDeathCallback = callback;
+ }
+ mExternalVibration.linkToDeath(this);
+ }
+
+ @Override
+ public void unlinkToDeath() {
+ mExternalVibration.unlinkToDeath(this);
+ synchronized (this) {
+ mBinderDeathCallback = null;
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (this) {
+ if (mBinderDeathCallback != null) {
+ mBinderDeathCallback.run();
+ }
+ }
+ }
+
+ @Override
+ void end(EndInfo endInfo) {
+ super.end(endInfo);
+ if (stats.hasStarted()) {
+ // External vibration doesn't have feedback from total time the vibrator was playing
+ // with non-zero amplitude, so we use the duration between start and end times of
+ // the vibration as the time the vibrator was ON, since the haptic channels are
+ // open for this duration and can receive vibration waveform data.
+ stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
+ }
+ }
+
+ @Override
+ public void notifyEnded() {
+ // Notify external client that this vibration should stop sending data to the vibrator.
+ mExternalVibration.mute();
+ }
+
+ boolean isHoldingSameVibration(ExternalVibration vib) {
+ return mExternalVibration.equals(vib);
+ }
+
+ void muteScale() {
+ mScale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+ if (Flags.hapticsScaleV2Enabled()) {
+ mScale.scaleFactor = 0;
+ }
+ }
+
+ void scale(VibrationScaler scaler, int usage) {
+ mScale.scaleLevel = scaler.getScaleLevel(usage);
+ if (Flags.hapticsScaleV2Enabled()) {
+ mScale.scaleFactor = scaler.getScaleFactor(usage);
+ }
+ mScale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
+ stats.reportAdaptiveScale(mScale.adaptiveHapticsScale);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index fe0cf59..ea4bd01 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -51,37 +51,22 @@
@NonNull
private volatile CombinedVibration mEffectToPlay;
- /** Vibration status. */
- private Vibration.Status mStatus;
-
/** Reported scale values applied to the vibration effects. */
private int mScaleLevel;
private float mAdaptiveScale;
HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect,
- @NonNull CallerInfo callerInfo) {
+ @NonNull VibrationSession.CallerInfo callerInfo) {
super(token, callerInfo);
mOriginalEffect = effect;
mEffectToPlay = effect;
- mStatus = Vibration.Status.RUNNING;
mScaleLevel = VibrationScaler.SCALE_NONE;
mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE;
}
- /**
- * Set the {@link Status} of this vibration and reports the current system time as this
- * vibration end time, for debugging purposes.
- *
- * <p>This method will only accept given value if the current status is {@link
- * Status#RUNNING}.
- */
- public void end(EndInfo info) {
- if (hasEnded()) {
- // Vibration already ended, keep first ending status set and ignore this one.
- return;
- }
- mStatus = info.status;
- stats.reportEnded(info.endedBy);
+ @Override
+ public void end(EndInfo endInfo) {
+ super.end(endInfo);
mCompletionLatch.countDown();
}
@@ -144,11 +129,6 @@
// No need to update fallback effects, they are already configured per device.
}
- /** Return true is current status is different from {@link Status#RUNNING}. */
- public boolean hasEnded() {
- return mStatus != Status.RUNNING;
- }
-
@Override
public boolean isRepeating() {
return mOriginalEffect.getDuration() == Long.MAX_VALUE;
@@ -159,16 +139,16 @@
return mEffectToPlay;
}
- /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */
- public Vibration.DebugInfo getDebugInfo() {
+ @Override
+ public VibrationSession.DebugInfo getDebugInfo() {
// Clear the original effect if it's the same as the effect that was played, for simplicity
CombinedVibration originalEffect =
Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect;
- return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect,
+ return new Vibration.DebugInfoImpl(getStatus(), stats, mEffectToPlay, originalEffect,
mScaleLevel, mAdaptiveScale, callerInfo);
}
- /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
+ @Override
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
int vibrationType = mEffectToPlay.hasVendorEffects()
? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__VENDOR
@@ -176,7 +156,7 @@
? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
: FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
return new VibrationStats.StatsInfo(
- callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus,
+ callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), getStatus(),
stats, completionUptimeMillis);
}
diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
index 4e58b9a..83e05f4 100644
--- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
+++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
@@ -26,6 +26,7 @@
import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
/** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */
final class InputDeviceDelegate implements InputManager.InputDeviceListener {
@@ -93,7 +94,7 @@
*
* @return {@link #isAvailable()}
*/
- public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) {
+ public boolean vibrateIfAvailable(CallerInfo callerInfo, CombinedVibration effect) {
synchronized (mLock) {
for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect,
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index aa4b9f3..9a04793 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -31,7 +31,6 @@
import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
-import java.io.PrintWriter;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -52,131 +51,69 @@
private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
public final long id;
- public final CallerInfo callerInfo;
+ public final VibrationSession.CallerInfo callerInfo;
public final VibrationStats stats = new VibrationStats();
public final IBinder callerToken;
- /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */
- enum Status {
- UNKNOWN(VibrationProto.UNKNOWN),
- RUNNING(VibrationProto.RUNNING),
- FINISHED(VibrationProto.FINISHED),
- FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED),
- FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES),
- CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED),
- CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF),
- CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE),
- CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER),
- CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER),
- CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON),
- CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED),
- CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS),
- IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS),
- IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING),
- IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING),
- IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN),
- IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS),
- IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND),
- IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION),
- IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED),
- IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL),
- IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE),
- IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING),
- IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER),
- IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE),
- IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS),
- IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED),
- IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE),
- IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER);
+ private VibrationSession.Status mStatus;
- private final int mProtoEnumValue;
-
- Status(int value) {
- mProtoEnumValue = value;
- }
-
- public int getProtoEnumValue() {
- return mProtoEnumValue;
- }
- }
-
- Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) {
+ Vibration(@NonNull IBinder token, @NonNull VibrationSession.CallerInfo callerInfo) {
Objects.requireNonNull(token);
Objects.requireNonNull(callerInfo);
+ mStatus = VibrationSession.Status.RUNNING;
this.id = sNextVibrationId.getAndIncrement();
this.callerToken = token;
this.callerInfo = callerInfo;
}
+ VibrationSession.Status getStatus() {
+ return mStatus;
+ }
+
+ /** Return true is current status is different from {@link VibrationSession.Status#RUNNING}. */
+ boolean hasEnded() {
+ return mStatus != VibrationSession.Status.RUNNING;
+ }
+
+ /**
+ * Set the {@link VibrationSession} of this vibration and reports the current system time as
+ * this vibration end time, for debugging purposes.
+ *
+ * <p>This method will only accept given value if the current status is {@link
+ * VibrationSession.Status#RUNNING}.
+ */
+ void end(Vibration.EndInfo endInfo) {
+ if (hasEnded()) {
+ // Vibration already ended, keep first ending status set and ignore this one.
+ return;
+ }
+ mStatus = endInfo.status;
+ stats.reportEnded(endInfo.endedBy);
+ }
+
/** Return true if vibration is a repeating vibration. */
abstract boolean isRepeating();
- /**
- * Holds lightweight immutable info on the process that triggered the vibration. This data
- * could potentially be kept in memory for a long time for bugreport dumpsys operations.
- *
- * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to
- * potentially expensive or resource-linked objects, such as {@link IBinder}.
- */
- static final class CallerInfo {
- public final VibrationAttributes attrs;
- public final int uid;
- public final int deviceId;
- public final String opPkg;
- public final String reason;
+ /** Return {@link VibrationSession.DebugInfo} with read-only debug data about this vibration. */
+ abstract VibrationSession.DebugInfo getDebugInfo();
- CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
- String reason) {
- Objects.requireNonNull(attrs);
- this.attrs = attrs;
- this.uid = uid;
- this.deviceId = deviceId;
- this.opPkg = opPkg;
- this.reason = reason;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof CallerInfo)) return false;
- CallerInfo that = (CallerInfo) o;
- return Objects.equals(attrs, that.attrs)
- && uid == that.uid
- && deviceId == that.deviceId
- && Objects.equals(opPkg, that.opPkg)
- && Objects.equals(reason, that.reason);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(attrs, uid, deviceId, opPkg, reason);
- }
-
- @Override
- public String toString() {
- return "CallerInfo{"
- + " uid=" + uid
- + ", opPkg=" + opPkg
- + ", deviceId=" + deviceId
- + ", attrs=" + attrs
- + ", reason=" + reason
- + '}';
- }
- }
+ /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
+ abstract VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis);
/** Immutable info passed as a signal to end a vibration. */
static final class EndInfo {
- /** The {@link Status} to be set to the vibration when it ends with this info. */
+ /** The vibration status to be set when it ends with this info. */
@NonNull
- public final Status status;
+ public final VibrationSession.Status status;
/** Info about the process that ended the vibration. */
- public final CallerInfo endedBy;
+ public final VibrationSession.CallerInfo endedBy;
- EndInfo(@NonNull Vibration.Status status) {
+ EndInfo(@NonNull VibrationSession.Status status) {
this(status, null);
}
- EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) {
+ EndInfo(@NonNull VibrationSession.Status status,
+ @Nullable VibrationSession.CallerInfo endedBy) {
this.status = status;
this.endedBy = endedBy;
}
@@ -211,10 +148,10 @@
* Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to
* potentially expensive or resource-linked objects, such as {@link IBinder}.
*/
- static final class DebugInfo {
- final Status mStatus;
+ static final class DebugInfoImpl implements VibrationSession.DebugInfo {
+ final VibrationSession.Status mStatus;
final long mCreateTime;
- final CallerInfo mCallerInfo;
+ final VibrationSession.CallerInfo mCallerInfo;
@Nullable
final CombinedVibration mPlayedEffect;
@@ -226,9 +163,10 @@
private final int mScaleLevel;
private final float mAdaptiveScale;
- DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
+ DebugInfoImpl(VibrationSession.Status status, VibrationStats stats,
+ @Nullable CombinedVibration playedEffect,
@Nullable CombinedVibration originalEffect, int scaleLevel,
- float adaptiveScale, @NonNull CallerInfo callerInfo) {
+ float adaptiveScale, @NonNull VibrationSession.CallerInfo callerInfo) {
Objects.requireNonNull(callerInfo);
mCreateTime = stats.getCreateTimeDebug();
mStartTime = stats.getStartTimeDebug();
@@ -243,6 +181,27 @@
}
@Override
+ public VibrationSession.Status getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ public long getCreateUptimeMillis() {
+ return mCreateTime;
+ }
+
+ @Override
+ public VibrationSession.CallerInfo getCallerInfo() {
+ return mCallerInfo;
+ }
+
+ @Nullable
+ @Override
+ public Object getDumpAggregationKey() {
+ return mPlayedEffect;
+ }
+
+ @Override
public String toString() {
return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true)
+ ", startTime: " + formatTime(mStartTime, /*includeDate=*/ true)
@@ -257,17 +216,13 @@
+ ", callerInfo: " + mCallerInfo;
}
- void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
+ @Override
+ public void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale);
}
- /**
- * Write this info in a compact way into given {@link PrintWriter}.
- *
- * <p>This is used by dumpsys to log multiple vibration records in single lines that are
- * easy to skim through by the sorted created time.
- */
- void dumpCompact(IndentingPrintWriter pw) {
+ @Override
+ public void dumpCompact(IndentingPrintWriter pw) {
boolean isExternalVibration = mPlayedEffect == null;
String timingsStr = String.format(Locale.ROOT,
"%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s",
@@ -299,8 +254,8 @@
pw.println(timingsStr + paramStr + audioUsageStr + callerStr + effectStr);
}
- /** Write this info into given {@link PrintWriter}. */
- void dump(IndentingPrintWriter pw) {
+ @Override
+ public void dump(IndentingPrintWriter pw) {
pw.println("Vibration:");
pw.increaseIndent();
pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
@@ -317,8 +272,8 @@
pw.decreaseIndent();
}
- /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
- void dump(ProtoOutputStream proto, long fieldId) {
+ @Override
+ public void dump(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(VibrationProto.START_TIME, mStartTime);
proto.write(VibrationProto.END_TIME, mEndTime);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java
new file mode 100644
index 0000000..5640b49
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibrationSession.java
@@ -0,0 +1,208 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CombinedVibration;
+import android.os.IBinder;
+import android.os.VibrationAttributes;
+import android.util.IndentingPrintWriter;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * Represents a generic vibration session that plays one or more vibration requests.
+ *
+ * <p>This might represent:
+ *
+ * <ol>
+ * <li>A single {@link CombinedVibration} playback.
+ * <li>An {@link android.os.ExternalVibration} playback.
+ * </ol>
+ */
+interface VibrationSession {
+
+ /** Returns data about the client app that triggered this vibration session. */
+ CallerInfo getCallerInfo();
+
+ /** Returns debug data for logging and metric reports. */
+ DebugInfo getDebugInfo();
+
+ /**
+ * Links this session to the app process death with given callback to handle it.
+ *
+ * <p>This can be used by the service to end the vibration session when the app process dies.
+ */
+ void linkToDeath(Runnable callback);
+
+ /** Removes link to the app process death. */
+ void unlinkToDeath();
+
+ /** Notify the session end was requested, which might be acted upon asynchronously. */
+ void notifyEnded();
+
+ /**
+ * Session status with reference to values from vibratormanagerservice.proto for logging.
+ */
+ enum Status {
+ UNKNOWN(VibrationProto.UNKNOWN),
+ RUNNING(VibrationProto.RUNNING),
+ FINISHED(VibrationProto.FINISHED),
+ FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED),
+ FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES),
+ CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED),
+ CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF),
+ CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE),
+ CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER),
+ CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER),
+ CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON),
+ CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED),
+ CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS),
+ IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS),
+ IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING),
+ IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING),
+ IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN),
+ IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS),
+ IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND),
+ IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION),
+ IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED),
+ IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL),
+ IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE),
+ IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING),
+ IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER),
+ IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE),
+ IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS),
+ IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED),
+ IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE),
+ IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER);
+
+ private final int mProtoEnumValue;
+
+ Status(int value) {
+ mProtoEnumValue = value;
+ }
+
+ public int getProtoEnumValue() {
+ return mProtoEnumValue;
+ }
+ }
+
+ /**
+ * Holds lightweight immutable info on the process that triggered the vibration session.
+ *
+ * <p>This data could potentially be kept in memory for a long time for bugreport dumpsys
+ * operations. It shouldn't hold any references to potentially expensive or resource-linked
+ * objects, such as {@link IBinder}.
+ */
+ final class CallerInfo {
+ public final VibrationAttributes attrs;
+ public final int uid;
+ public final int deviceId;
+ public final String opPkg;
+ public final String reason;
+
+ CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
+ String reason) {
+ Objects.requireNonNull(attrs);
+ this.attrs = attrs;
+ this.uid = uid;
+ this.deviceId = deviceId;
+ this.opPkg = opPkg;
+ this.reason = reason;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CallerInfo)) return false;
+ CallerInfo that = (CallerInfo) o;
+ return Objects.equals(attrs, that.attrs)
+ && uid == that.uid
+ && deviceId == that.deviceId
+ && Objects.equals(opPkg, that.opPkg)
+ && Objects.equals(reason, that.reason);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(attrs, uid, deviceId, opPkg, reason);
+ }
+
+ @Override
+ public String toString() {
+ return "CallerInfo{"
+ + " uid=" + uid
+ + ", opPkg=" + opPkg
+ + ", deviceId=" + deviceId
+ + ", attrs=" + attrs
+ + ", reason=" + reason
+ + '}';
+ }
+ }
+
+ /**
+ * Interface for lightweight debug information about the vibration session for debugging.
+ *
+ * <p>This data could potentially be kept in memory for a long time for bugreport dumpsys
+ * operations. It shouldn't hold any references to potentially expensive or resource-linked
+ * objects, such as {@link IBinder}.
+ */
+ interface DebugInfo {
+
+ /** Return the vibration session status. */
+ Status getStatus();
+
+ /** Returns the session creation time from {@link android.os.SystemClock#uptimeMillis()}. */
+ long getCreateUptimeMillis();
+
+ /** Returns information about the process that created the session. */
+ CallerInfo getCallerInfo();
+
+ /**
+ * Returns the aggregation key for log records.
+ *
+ * <p>This is used to aggregate similar vibration sessions triggered in quick succession
+ * (e.g. multiple keyboard vibrations when the user is typing).
+ *
+ * <p>This does not need to include data from {@link CallerInfo} or {@link Status}.
+ *
+ * @see GroupedAggregatedLogRecords
+ */
+ @Nullable
+ Object getDumpAggregationKey();
+
+ /** Logs vibration session fields for metric reports. */
+ void logMetrics(VibratorFrameworkStatsLogger statsLogger);
+
+ /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
+ void dump(ProtoOutputStream proto, long fieldId);
+
+ /** Write this info into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw);
+
+ /**
+ * Write this info in a compact way into given {@link PrintWriter}.
+ *
+ * <p>This is used by dumpsys to log multiple records in single lines that are easy to skim
+ * through by the sorted created time.
+ */
+ void dumpCompact(IndentingPrintWriter pw);
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 0d6778c..69cdcf4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -66,6 +66,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.Status;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -416,46 +418,46 @@
/**
* Check if given vibration should be ignored by the service.
*
- * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored,
+ * @return One of VibrationSession.Status.IGNORED_* values if the vibration should be ignored,
* null otherwise.
*/
@Nullable
- public Vibration.Status shouldIgnoreVibration(@NonNull Vibration.CallerInfo callerInfo) {
+ public Status shouldIgnoreVibration(@NonNull CallerInfo callerInfo) {
final int usage = callerInfo.attrs.getUsage();
synchronized (mLock) {
if (!mUidObserver.isUidForeground(callerInfo.uid)
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
- return Vibration.Status.IGNORED_BACKGROUND;
+ return Status.IGNORED_BACKGROUND;
}
if (callerInfo.deviceId != Context.DEVICE_ID_DEFAULT
&& callerInfo.deviceId != Context.DEVICE_ID_INVALID) {
- return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+ return Status.IGNORED_FROM_VIRTUAL_DEVICE;
}
if (callerInfo.deviceId == Context.DEVICE_ID_INVALID
&& isAppRunningOnAnyVirtualDevice(callerInfo.uid)) {
- return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+ return Status.IGNORED_FROM_VIRTUAL_DEVICE;
}
if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) {
- return Vibration.Status.IGNORED_FOR_POWER;
+ return Status.IGNORED_FOR_POWER;
}
if (!callerInfo.attrs.isFlagSet(
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
&& !shouldVibrateForUserSetting(callerInfo)) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
+ return Status.IGNORED_FOR_SETTINGS;
}
if (!callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
if (!shouldVibrateForRingerModeLocked(usage)) {
- return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+ return Status.IGNORED_FOR_RINGER_MODE;
}
}
if (mVibrationConfig.ignoreVibrationsOnWirelessCharger() && mOnWirelessCharger) {
- return Vibration.Status.IGNORED_ON_WIRELESS_CHARGER;
+ return Status.IGNORED_ON_WIRELESS_CHARGER;
}
}
return null;
@@ -471,7 +473,7 @@
*
* @return true if the vibration should be cancelled when the screen goes off, false otherwise.
*/
- public boolean shouldCancelVibrationOnScreenOff(@NonNull Vibration.CallerInfo callerInfo,
+ public boolean shouldCancelVibrationOnScreenOff(@NonNull CallerInfo callerInfo,
long vibrationStartUptimeMillis) {
PowerManagerInternal pm;
synchronized (mLock) {
@@ -483,8 +485,8 @@
// ignored here and not cancel a vibration, and those are usually triggered by timeout
// or inactivity, so it's unlikely that it will override a more active goToSleep reason.
PowerManager.SleepData sleepData = pm.getLastGoToSleep();
- if ((sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis)
- || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason)) {
+ if (sleepData != null && (sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis
+ || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason))) {
// Ignore screen off events triggered before the vibration started, and all
// automatic "go to sleep" events from allowlist.
Slog.d(TAG, "Ignoring screen off event triggered at uptime "
@@ -522,7 +524,7 @@
* {@code false} to ignore the vibration.
*/
@GuardedBy("mLock")
- private boolean shouldVibrateForUserSetting(Vibration.CallerInfo callerInfo) {
+ private boolean shouldVibrateForUserSetting(CallerInfo callerInfo) {
final int usage = callerInfo.attrs.getUsage();
if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
// Main setting disabled.
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index 8179d6a..fc0c6e7 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -166,7 +166,7 @@
* @return true if the status was accepted. This method will only accept given values if
* the end timestamp was never set.
*/
- boolean reportEnded(@Nullable Vibration.CallerInfo endedBy) {
+ boolean reportEnded(@Nullable VibrationSession.CallerInfo endedBy) {
if (hasEnded()) {
// Vibration already ended, keep first ending stats set and ignore this one.
return false;
@@ -187,7 +187,7 @@
* <p>This method will only accept the first value as the one that was interrupted by this
* vibration, and will ignore all successive calls.
*/
- void reportInterruptedAnotherVibration(@NonNull Vibration.CallerInfo callerInfo) {
+ void reportInterruptedAnotherVibration(@NonNull VibrationSession.CallerInfo callerInfo) {
if (mInterruptedUsage < 0) {
mInterruptedUsage = callerInfo.attrs.getUsage();
}
@@ -330,7 +330,7 @@
public final int[] halUnsupportedEffectsUsed;
private boolean mIsWritten;
- StatsInfo(int uid, int vibrationType, int usage, Vibration.Status status,
+ StatsInfo(int uid, int vibrationType, int usage, VibrationSession.Status status,
VibrationStats stats, long completionUptimeMillis) {
this.uid = uid;
this.vibrationType = vibrationType;
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 7152844..5137d19 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -32,6 +32,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.vibrator.VibrationSession.Status;
import java.util.ArrayList;
import java.util.Iterator;
@@ -217,7 +218,7 @@
}
/**
- * Calculate the {@link Vibration.Status} based on the current queue state and the expected
+ * Calculate the {@link Vibration.EndInfo} based on the current queue state and the expected
* number of {@link StartSequentialEffectStep} to be played.
*/
@Nullable
@@ -235,10 +236,10 @@
}
// No pending steps, and something happened.
if (mSuccessfulVibratorOnSteps > 0) {
- return new Vibration.EndInfo(Vibration.Status.FINISHED);
+ return new Vibration.EndInfo(Status.FINISHED);
}
// If no step was able to turn the vibrator ON successfully.
- return new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED);
+ return new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED);
}
/**
@@ -352,7 +353,7 @@
if (DEBUG) {
Slog.d(TAG, "Binder died, cancelling vibration...");
}
- notifyCancelled(new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
+ notifyCancelled(new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED),
/* immediate= */ false);
}
@@ -377,7 +378,7 @@
if ((cancelInfo == null) || !cancelInfo.status.name().startsWith("CANCEL")) {
Slog.w(TAG, "Vibration cancel requested with bad signal=" + cancelInfo
+ ", using CANCELLED_UNKNOWN_REASON to ensure cancellation.");
- cancelInfo = new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_UNKNOWN_REASON);
+ cancelInfo = new Vibration.EndInfo(Status.CANCELLED_BY_UNKNOWN_REASON);
}
synchronized (mLock) {
if ((immediate && mSignalCancelImmediate) || (mSignalCancel != null)) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index cfb4c74..ab4a4d8 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vibrator.VibrationSession.Status;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -240,7 +241,7 @@
runCurrentVibrationWithWakeLockAndDeathLink();
} finally {
clientVibrationCompleteIfNotAlready(
- new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED));
+ new Vibration.EndInfo(Status.FINISHED_UNEXPECTED));
}
} finally {
mWakeLock.release();
@@ -259,7 +260,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "Error linking vibration to token death", e);
clientVibrationCompleteIfNotAlready(
- new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_TOKEN));
+ new Vibration.EndInfo(Status.IGNORED_ERROR_TOKEN));
return;
}
// Ensure that the unlink always occurs now.
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index de5e662..3a814cd 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -559,8 +559,8 @@
}
/**
- * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
- * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
+ * Record for a single {@link VibrationSession.DebugInfo}, that can be grouped by usage and
+ * aggregated by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
*/
private static final class VibrationScaleParamRecord
implements GroupedAggregatedLogRecords.SingleLogRecord {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index c143beb..799934a 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -73,9 +73,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.SystemService;
import com.android.server.pm.BackgroundUserSoundNotifier;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.DebugInfo;
+import com.android.server.vibrator.VibrationSession.Status;
import libcore.util.NativeAllocationRegistry;
@@ -160,11 +162,12 @@
@GuardedBy("mLock")
private VibrationStepConductor mNextVibration;
@GuardedBy("mLock")
- private ExternalVibrationHolder mCurrentExternalVibration;
+ private ExternalVibrationSession mCurrentExternalVibration;
@GuardedBy("mLock")
private boolean mServiceReady;
- private final VibrationSettings mVibrationSettings;
+ @VisibleForTesting
+ final VibrationSettings mVibrationSettings;
private final VibrationScaler mVibrationScaler;
private final VibratorControlService mVibratorControlService;
private final InputDeviceDelegate mInputDeviceDelegate;
@@ -184,13 +187,12 @@
// When the system is entering a non-interactive state, we want to cancel
// vibrations in case a misbehaving app has abandoned them.
if (shouldCancelOnScreenOffLocked(mNextVibration)) {
- clearNextVibrationLocked(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF));
+ clearNextVibrationLocked(new Vibration.EndInfo(
+ Status.CANCELLED_BY_SCREEN_OFF));
}
if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
- mCurrentVibration.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
- /* immediate= */ false);
+ mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
+ Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false);
}
}
} else if (android.multiuser.Flags.addUiForSoundsFromBackgroundUsers()
@@ -198,12 +200,11 @@
synchronized (mLock) {
if (shouldCancelOnFgUserRequest(mNextVibration)) {
clearNextVibrationLocked(new Vibration.EndInfo(
- Vibration.Status.CANCELLED_BY_FOREGROUND_USER));
+ Status.CANCELLED_BY_FOREGROUND_USER));
}
if (shouldCancelOnFgUserRequest(mCurrentVibration)) {
mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
- Vibration.Status.CANCELLED_BY_FOREGROUND_USER),
- /* immediate= */ false);
+ Status.CANCELLED_BY_FOREGROUND_USER), /* immediate= */ false);
}
}
}
@@ -220,12 +221,12 @@
}
synchronized (mLock) {
if (shouldCancelAppOpModeChangedLocked(mNextVibration)) {
- clearNextVibrationLocked(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_APP_OPS));
+ clearNextVibrationLocked(new Vibration.EndInfo(
+ Status.CANCELLED_BY_APP_OPS));
}
if (shouldCancelAppOpModeChangedLocked(mCurrentVibration)) {
mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
- Vibration.Status.CANCELLED_BY_APP_OPS), /* immediate= */ false);
+ Status.CANCELLED_BY_APP_OPS), /* immediate= */ false);
}
}
}
@@ -441,8 +442,8 @@
return false;
}
AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
- new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg,
- null), effects);
+ new CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg, null),
+ effects);
mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
updateAlwaysOnLocked(alwaysOnVibration);
}
@@ -490,8 +491,7 @@
// Make sure we report the constant id in the requested haptic feedback reason.
reason = "performHapticFeedback(constant=" + constant + "): " + reason;
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
- Vibration.Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason,
- hapticVibrationProvider);
+ Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider);
if (ignoreStatus != null) {
logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus);
return null;
@@ -516,8 +516,7 @@
reason = "performHapticFeedbackForInputDevice(constant=" + constant + ", inputDeviceId="
+ inputDeviceId + ", inputSource=" + inputSource + "): " + reason;
HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
- Vibration.Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason,
- hapticVibrationProvider);
+ Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider);
if (ignoreStatus != null) {
logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus);
return null;
@@ -533,7 +532,7 @@
VibrationAttributes attrs) {
if (effect == null) {
logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason,
- Vibration.Status.IGNORED_UNSUPPORTED);
+ Status.IGNORED_UNSUPPORTED);
Slog.w(TAG,
"performHapticFeedbackWithEffect; vibration absent for constant " + constant);
return null;
@@ -578,23 +577,21 @@
private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
@NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
String reason, IBinder token) {
- Vibration.CallerInfo callerInfo =
- new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason);
+ CallerInfo callerInfo = new CallerInfo(attrs, uid, deviceId, opPkg, reason);
if (token == null) {
Slog.e(TAG, "token must not be null");
- logAndRecordVibrationAttempt(effect, callerInfo, Vibration.Status.IGNORED_ERROR_TOKEN);
+ logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_ERROR_TOKEN);
return null;
}
if (effect.hasVendorEffects()
&& !hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) {
Slog.e(TAG, "vibrate; no permission for vendor effects");
- logAndRecordVibrationAttempt(effect, callerInfo,
- Vibration.Status.IGNORED_MISSING_PERMISSION);
+ logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_MISSING_PERMISSION);
return null;
}
enforceUpdateAppOpsStatsPermission(uid);
if (!isEffectValid(effect)) {
- logAndRecordVibrationAttempt(effect, callerInfo, Vibration.Status.IGNORED_UNSUPPORTED);
+ logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED);
return null;
}
// Create Vibration.Stats as close to the received request as possible, for tracking.
@@ -625,11 +622,11 @@
final long ident = Binder.clearCallingIdentity();
try {
if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.mute();
+ mCurrentExternalVibration.notifyEnded();
vib.stats.reportInterruptedAnotherVibration(
mCurrentExternalVibration.callerInfo);
endExternalVibrateLocked(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
vib.callerInfo),
/* continueExternalControl= */ false);
} else if (mCurrentVibration != null) {
@@ -645,7 +642,7 @@
vib.stats.reportInterruptedAnotherVibration(
mCurrentVibration.getVibration().callerInfo);
mCurrentVibration.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+ new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
vib.callerInfo),
/* immediate= */ false);
}
@@ -677,7 +674,7 @@
Slog.d(TAG, "Canceling vibration");
}
Vibration.EndInfo cancelledByUserInfo =
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER);
final long ident = Binder.clearCallingIdentity();
try {
if (mNextVibration != null
@@ -693,9 +690,9 @@
}
if (mCurrentExternalVibration != null
&& shouldCancelVibration(
- mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
+ mCurrentExternalVibration.getCallerInfo().attrs,
usageFilter)) {
- mCurrentExternalVibration.mute();
+ mCurrentExternalVibration.notifyEnded();
endExternalVibrateLocked(
cancelledByUserInfo, /* continueExternalControl= */ false);
}
@@ -860,7 +857,7 @@
: vibrationEndInfo.status));
}
mCurrentVibration.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE),
/* immediate= */ false);
}
}
@@ -911,7 +908,7 @@
// Note that we don't consider pipelining here, because new pipelined ones should
// replace pending non-executing pipelined ones anyway.
clearNextVibrationLocked(
- new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED, vib.callerInfo));
+ new Vibration.EndInfo(Status.IGNORED_SUPERSEDED, vib.callerInfo));
mNextVibration = conductor;
return null;
} finally {
@@ -934,15 +931,15 @@
if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) {
// Shouldn't happen. The method call already logs a wtf.
mCurrentVibration = null; // Aborted.
- return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_SCHEDULING);
+ return new Vibration.EndInfo(Status.IGNORED_ERROR_SCHEDULING);
}
return null;
case AppOpsManager.MODE_ERRORED:
Slog.w(TAG, "Start AppOpsManager operation errored for uid "
+ vib.callerInfo.uid);
- return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS);
+ return new Vibration.EndInfo(Status.IGNORED_ERROR_APP_OPS);
default:
- return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS);
+ return new Vibration.EndInfo(Status.IGNORED_APP_OPS);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -950,7 +947,7 @@
}
@GuardedBy("mLock")
- private void endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo,
+ private void endVibrationLocked(Vibration vib, Vibration.EndInfo vibrationEndInfo,
boolean shouldWriteStats) {
vib.end(vibrationEndInfo);
logAndRecordVibration(vib.getDebugInfo());
@@ -960,15 +957,6 @@
}
}
- @GuardedBy("mLock")
- private void endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib,
- Vibration.EndInfo vibrationEndInfo) {
- vib.end(vibrationEndInfo);
- logAndRecordVibration(vib.getDebugInfo());
- mFrameworkStatsLogger.writeVibrationReportedAsync(
- vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
- }
-
private VibrationStepConductor createVibrationStepConductor(HalVibration vib) {
CompletableFuture<Void> requestVibrationParamsFuture = null;
@@ -990,33 +978,32 @@
vib.scaleEffects(mVibrationScaler);
mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay());
- return new Vibration.EndInfo(Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
+ return new Vibration.EndInfo(Status.FORWARDED_TO_INPUT_DEVICES);
}
private void logAndRecordPerformHapticFeedbackAttempt(int uid, int deviceId, String opPkg,
- String reason, Vibration.Status status) {
- Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+ String reason, Status status) {
+ CallerInfo callerInfo = new CallerInfo(
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_UNKNOWN),
uid, deviceId, opPkg, reason);
logAndRecordVibrationAttempt(/* effect= */ null, callerInfo, status);
}
private void logAndRecordVibrationAttempt(@Nullable CombinedVibration effect,
- Vibration.CallerInfo callerInfo, Vibration.Status status) {
+ CallerInfo callerInfo, Status status) {
logAndRecordVibration(
- new Vibration.DebugInfo(status, new VibrationStats(),
+ new Vibration.DebugInfoImpl(status, new VibrationStats(),
effect, /* originalEffect= */ null, VibrationScaler.SCALE_NONE,
VibrationScaler.ADAPTIVE_SCALE_NONE, callerInfo));
}
- private void logAndRecordVibration(Vibration.DebugInfo info) {
+ private void logAndRecordVibration(DebugInfo info) {
info.logMetrics(mFrameworkStatsLogger);
- logVibrationStatus(info.mCallerInfo.uid, info.mCallerInfo.attrs, info.mStatus);
+ logVibrationStatus(info.getCallerInfo().uid, info.getCallerInfo().attrs, info.getStatus());
mVibratorManagerRecords.record(info);
}
- private void logVibrationStatus(int uid, VibrationAttributes attrs,
- Vibration.Status status) {
+ private void logVibrationStatus(int uid, VibrationAttributes attrs, Status status) {
switch (status) {
case IGNORED_BACKGROUND:
Slog.e(TAG, "Ignoring incoming vibration as process with"
@@ -1161,15 +1148,14 @@
if (ongoingVibrationImportance > newVibrationImportance) {
// Existing vibration has higher importance and should not be cancelled.
- return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE,
+ return new Vibration.EndInfo(Status.IGNORED_FOR_HIGHER_IMPORTANCE,
ongoingVibration.callerInfo);
}
// Same importance, use repeating as a tiebreaker.
if (ongoingVibration.isRepeating() && !newVibration.isRepeating()) {
// Ongoing vibration is repeating and new one is not, give priority to ongoing
- return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_ONGOING,
- ongoingVibration.callerInfo);
+ return new Vibration.EndInfo(Status.IGNORED_FOR_ONGOING, ongoingVibration.callerInfo);
}
// New vibration is repeating or this is a complete tie between them,
// give priority to new vibration.
@@ -1220,8 +1206,8 @@
*/
@GuardedBy("mLock")
@Nullable
- private Vibration.EndInfo shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo) {
- Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo);
+ private Vibration.EndInfo shouldIgnoreVibrationLocked(CallerInfo callerInfo) {
+ Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo);
if (statusFromSettings != null) {
return new Vibration.EndInfo(statusFromSettings);
}
@@ -1231,9 +1217,9 @@
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually
// want to throw a SecurityException here.
- return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS);
+ return new Vibration.EndInfo(Status.IGNORED_ERROR_APP_OPS);
} else {
- return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS);
+ return new Vibration.EndInfo(Status.IGNORED_APP_OPS);
}
}
@@ -1241,16 +1227,16 @@
}
@Nullable
- private Vibration.Status shouldIgnoreHapticFeedback(int constant, String reason,
+ private Status shouldIgnoreHapticFeedback(int constant, String reason,
HapticFeedbackVibrationProvider hapticVibrationProvider) {
if (hapticVibrationProvider == null) {
Slog.e(TAG, reason + "; haptic vibration provider not ready.");
- return Vibration.Status.IGNORED_ERROR_SCHEDULING;
+ return Status.IGNORED_ERROR_SCHEDULING;
}
if (hapticVibrationProvider.isRestrictedHapticFeedback(constant)
&& !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) {
Slog.w(TAG, reason + "; no permission for system constant " + constant);
- return Vibration.Status.IGNORED_MISSING_PERMISSION;
+ return Status.IGNORED_MISSING_PERMISSION;
}
return null;
}
@@ -1291,7 +1277,7 @@
* {@code attrs}. This will return one of the AppOpsManager.MODE_*.
*/
@GuardedBy("mLock")
- private int checkAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+ private int checkAppOpModeLocked(CallerInfo callerInfo) {
int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg);
int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs);
@@ -1306,7 +1292,7 @@
/** Start an operation in {@link AppOpsManager}, if allowed. */
@GuardedBy("mLock")
- private int startAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+ private int startAppOpModeLocked(CallerInfo callerInfo) {
return fixupAppOpModeLocked(
mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg),
callerInfo.attrs);
@@ -1317,7 +1303,7 @@
* operation with same uid was previously started.
*/
@GuardedBy("mLock")
- private void finishAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+ private void finishAppOpModeLocked(CallerInfo callerInfo) {
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg);
}
@@ -1735,10 +1721,10 @@
*/
private static final class AlwaysOnVibration {
public final int alwaysOnId;
- public final Vibration.CallerInfo callerInfo;
+ public final CallerInfo callerInfo;
public final SparseArray<PrebakedSegment> effects;
- AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo,
+ AlwaysOnVibration(int alwaysOnId, CallerInfo callerInfo,
SparseArray<PrebakedSegment> effects) {
this.alwaysOnId = alwaysOnId;
this.callerInfo = callerInfo;
@@ -1746,113 +1732,6 @@
}
}
- /** Holder for a {@link ExternalVibration}. */
- private final class ExternalVibrationHolder extends Vibration implements
- IBinder.DeathRecipient {
-
- public final ExternalVibration externalVibration;
- public final ExternalVibrationScale scale = new ExternalVibrationScale();
-
- private Vibration.Status mStatus;
-
- private ExternalVibrationHolder(ExternalVibration externalVibration) {
- super(externalVibration.getToken(), new Vibration.CallerInfo(
- externalVibration.getVibrationAttributes(), externalVibration.getUid(),
- // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
- // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
- Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
- this.externalVibration = externalVibration;
- mStatus = Vibration.Status.RUNNING;
- }
-
- public void muteScale() {
- scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
- if (Flags.hapticsScaleV2Enabled()) {
- scale.scaleFactor = 0;
- }
- }
-
- public void scale(VibrationScaler scaler, int usage) {
- scale.scaleLevel = scaler.getScaleLevel(usage);
- if (Flags.hapticsScaleV2Enabled()) {
- scale.scaleFactor = scaler.getScaleFactor(usage);
- }
- scale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
- stats.reportAdaptiveScale(scale.adaptiveHapticsScale);
- }
-
- public void mute() {
- externalVibration.mute();
- }
-
- public void linkToDeath() {
- externalVibration.linkToDeath(this);
- }
-
- public void unlinkToDeath() {
- externalVibration.unlinkToDeath(this);
- }
-
- public boolean isHoldingSameVibration(ExternalVibration externalVibration) {
- return this.externalVibration.equals(externalVibration);
- }
-
- public void end(Vibration.EndInfo info) {
- if (mStatus != Vibration.Status.RUNNING) {
- // Already ended, ignore this call
- return;
- }
- mStatus = info.status;
- stats.reportEnded(info.endedBy);
-
- if (stats.hasStarted()) {
- // External vibration doesn't have feedback from total time the vibrator was playing
- // with non-zero amplitude, so we use the duration between start and end times of
- // the vibration as the time the vibrator was ON, since the haptic channels are
- // open for this duration and can receive vibration waveform data.
- stats.reportVibratorOn(
- stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
- }
- }
-
- public void binderDied() {
- synchronized (mLock) {
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "External vibration finished because binder died");
- }
- endExternalVibrateLocked(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
- /* continueExternalControl= */ false);
- }
- }
- }
-
- public Vibration.DebugInfo getDebugInfo() {
- return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
- /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale,
- callerInfo);
- }
-
- public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
- return new VibrationStats.StatsInfo(
- externalVibration.getUid(),
- FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
- externalVibration.getVibrationAttributes().getUsage(), mStatus, stats,
- completionUptimeMillis);
- }
-
- @Override
- boolean isRepeating() {
- // We don't currently know if the external vibration is repeating, so we just use a
- // heuristic based on the usage. Ideally this would be propagated in the
- // ExternalVibration.
- int usage = externalVibration.getVibrationAttributes().getUsage();
- return usage == VibrationAttributes.USAGE_RINGTONE
- || usage == VibrationAttributes.USAGE_ALARM;
- }
- }
-
/** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */
@VisibleForTesting
public static class NativeWrapper {
@@ -1912,7 +1791,7 @@
new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
}
- synchronized void record(Vibration.DebugInfo info) {
+ synchronized void record(DebugInfo info) {
GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord =
mRecentVibrations.add(new VibrationRecord(info));
if (droppedRecord != null) {
@@ -1969,25 +1848,25 @@
}
/**
- * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
- * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
+ * Record for a single {@link DebugInfo}, that can be grouped by usage and aggregated by UID,
+ * {@link VibrationAttributes} and {@link CombinedVibration}.
*/
private static final class VibrationRecord
implements GroupedAggregatedLogRecords.SingleLogRecord {
- private final Vibration.DebugInfo mInfo;
+ private final DebugInfo mInfo;
- VibrationRecord(Vibration.DebugInfo info) {
+ VibrationRecord(DebugInfo info) {
mInfo = info;
}
@Override
public int getGroupKey() {
- return mInfo.mCallerInfo.attrs.getUsage();
+ return mInfo.getCallerInfo().attrs.getUsage();
}
@Override
public long getCreateUptimeMs() {
- return mInfo.mCreateTime;
+ return mInfo.getCreateUptimeMillis();
}
@Override
@@ -1995,10 +1874,10 @@
if (!(record instanceof VibrationRecord)) {
return false;
}
- Vibration.DebugInfo info = ((VibrationRecord) record).mInfo;
- return mInfo.mCallerInfo.uid == info.mCallerInfo.uid
- && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs)
- && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect);
+ DebugInfo info = ((VibrationRecord) record).mInfo;
+ return mInfo.getCallerInfo().uid == info.getCallerInfo().uid
+ && Objects.equals(mInfo.getCallerInfo().attrs, info.getCallerInfo().attrs)
+ && Objects.equals(mInfo.getDumpAggregationKey(), info.getDumpAggregationKey());
}
@Override
@@ -2048,7 +1927,8 @@
setExternalControl(false, mCurrentExternalVibration.stats);
}
// The external control was turned off, end it and report metrics right away.
- endVibrationAndWriteStatsLocked(mCurrentExternalVibration, vibrationEndInfo);
+ endVibrationLocked(mCurrentExternalVibration, vibrationEndInfo,
+ /* shouldWriteStats= */ true);
mCurrentExternalVibration = null;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -2108,17 +1988,18 @@
@Override
public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
// Create Vibration.Stats as close to the received request as possible, for tracking.
- ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+ ExternalVibrationSession externalVibration = new ExternalVibrationSession(vib);
// Mute the request until we run all the checks and accept the vibration.
- vibHolder.muteScale();
+ externalVibration.muteScale();
boolean alreadyUnderExternalControl = false;
boolean waitForCompletion = false;
synchronized (mLock) {
if (!hasExternalControlCapability()) {
- endVibrationAndWriteStatsLocked(vibHolder,
- new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED));
- return vibHolder.scale;
+ endVibrationLocked(externalVibration,
+ new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED),
+ /* shouldWriteStats= */ true);
+ return externalVibration.getScale();
}
if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
@@ -2127,44 +2008,46 @@
Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ " tried to play externally controlled vibration"
+ " without VIBRATE permission, ignoring.");
- endVibrationAndWriteStatsLocked(vibHolder,
- new Vibration.EndInfo(Vibration.Status.IGNORED_MISSING_PERMISSION));
- return vibHolder.scale;
+ endVibrationLocked(externalVibration,
+ new Vibration.EndInfo(Status.IGNORED_MISSING_PERMISSION),
+ /* shouldWriteStats= */ true);
+ return externalVibration.getScale();
}
Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(
- vibHolder.callerInfo);
+ externalVibration.callerInfo);
if (vibrationEndInfo == null
&& mCurrentExternalVibration != null
&& mCurrentExternalVibration.isHoldingSameVibration(vib)) {
// We are already playing this external vibration, so we can return the same
// scale calculated in the previous call to this method.
- return mCurrentExternalVibration.scale;
+ return mCurrentExternalVibration.getScale();
}
if (vibrationEndInfo == null) {
// Check if ongoing vibration is more important than this vibration.
- vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vibHolder);
+ vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(externalVibration);
}
if (vibrationEndInfo != null) {
- endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
- return vibHolder.scale;
+ endVibrationLocked(externalVibration, vibrationEndInfo,
+ /* shouldWriteStats= */ true);
+ return externalVibration.getScale();
}
if (mCurrentExternalVibration == null) {
// If we're not under external control right now, then cancel any normal
// vibration that may be playing and ready the vibrator for external control.
if (mCurrentVibration != null) {
- vibHolder.stats.reportInterruptedAnotherVibration(
+ externalVibration.stats.reportInterruptedAnotherVibration(
mCurrentVibration.getVibration().callerInfo);
clearNextVibrationLocked(
- new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL,
- vibHolder.callerInfo));
+ new Vibration.EndInfo(Status.IGNORED_FOR_EXTERNAL,
+ externalVibration.callerInfo));
mCurrentVibration.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
- vibHolder.callerInfo),
+ new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
+ externalVibration.callerInfo),
/* immediate= */ true);
waitForCompletion = true;
}
@@ -2178,12 +2061,12 @@
// Note that this doesn't support multiple concurrent external controls, as we
// would need to mute the old one still if it came from a different controller.
alreadyUnderExternalControl = true;
- mCurrentExternalVibration.mute();
- vibHolder.stats.reportInterruptedAnotherVibration(
+ mCurrentExternalVibration.notifyEnded();
+ externalVibration.stats.reportInterruptedAnotherVibration(
mCurrentExternalVibration.callerInfo);
endExternalVibrateLocked(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
- vibHolder.callerInfo),
+ new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
+ externalVibration.callerInfo),
/* continueExternalControl= */ true);
}
@@ -2195,9 +2078,9 @@
mVibrationSettings.update();
}
- mCurrentExternalVibration = vibHolder;
- vibHolder.linkToDeath();
- vibHolder.scale(mVibrationScaler, attrs.getUsage());
+ mCurrentExternalVibration = externalVibration;
+ externalVibration.linkToDeath(this::onExternalVibrationBinderDied);
+ externalVibration.scale(mVibrationScaler, attrs.getUsage());
}
if (waitForCompletion) {
@@ -2206,27 +2089,27 @@
synchronized (mLock) {
// Trigger endExternalVibrateLocked to unlink to death recipient.
endExternalVibrateLocked(
- new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
+ new Vibration.EndInfo(Status.IGNORED_ERROR_CANCELLING),
/* continueExternalControl= */ false);
// Mute the request, vibration will be ignored.
- vibHolder.muteScale();
+ externalVibration.muteScale();
}
- return vibHolder.scale;
+ return externalVibration.getScale();
}
}
if (!alreadyUnderExternalControl) {
if (DEBUG) {
Slog.d(TAG, "Vibrator going under external control.");
}
- setExternalControl(true, vibHolder.stats);
+ setExternalControl(true, externalVibration.stats);
}
if (DEBUG) {
Slog.d(TAG, "Playing external vibration: " + vib);
}
// Vibrator will start receiving data from external channels after this point.
// Report current time as the vibration start time, for debugging.
- vibHolder.stats.reportStarted();
- return vibHolder.scale;
+ externalVibration.stats.reportStarted();
+ return externalVibration.getScale();
}
@Override
@@ -2238,7 +2121,7 @@
Slog.d(TAG, "Stopping external vibration: " + vib);
}
endExternalVibrateLocked(
- new Vibration.EndInfo(Vibration.Status.FINISHED),
+ new Vibration.EndInfo(Status.FINISHED),
/* continueExternalControl= */ false);
}
}
@@ -2252,6 +2135,19 @@
}
return false;
}
+
+ private void onExternalVibrationBinderDied() {
+ synchronized (mLock) {
+ if (mCurrentExternalVibration != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "External vibration finished because binder died");
+ }
+ endExternalVibrateLocked(
+ new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED),
+ /* continueExternalControl= */ false);
+ }
+ }
+ }
}
/** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index ba2594a..f53dda6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -42,6 +42,7 @@
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_LOCK_ORIG;
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
+import static com.android.window.flags.Flags.avoidRebindingIntentionallyDisconnectedWallpaper;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.offloadColorExtraction;
@@ -897,6 +898,12 @@
return;
}
+ if (avoidRebindingIntentionallyDisconnectedWallpaper()
+ && mWallpaper.connection == null) {
+ Slog.w(TAG, "Trying to reset an intentionally disconnected wallpaper!");
+ return;
+ }
+
if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
@@ -1066,6 +1073,13 @@
if (mWallpaper.wallpaperUpdating) {
return;
}
+
+ if (avoidRebindingIntentionallyDisconnectedWallpaper()
+ && mWallpaper.connection == null) {
+ Slog.w(TAG, "Trying to rebind an intentionally disconnected wallpaper!");
+ return;
+ }
+
final ComponentName wpService = mWallpaper.wallpaperComponent;
// The broadcast of package update could be delayed after service disconnected. Try
// to re-bind the service for 10 seconds.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 6bfa32a..7f6dc84 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1057,9 +1057,8 @@
* needs to be passed/applied in shell because until finish is called, shell owns the surfaces.
* Additionally, this gives shell the ability to better deal with merged transitions.
*/
- private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info) {
- // usually only size 1
- final ArraySet<DisplayContent> displays = new ArraySet<>();
+ private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info,
+ DisplayContent[] participantDisplays) {
for (int i = mTargets.size() - 1; i >= 0; --i) {
final WindowContainer<?> target = mTargets.get(i).mContainer;
if (target.getParent() == null) continue;
@@ -1071,7 +1070,6 @@
t.setCornerRadius(targetLeash, 0);
t.setShadowRadius(targetLeash, 0);
t.setAlpha(targetLeash, 1);
- displays.add(target.getDisplayContent());
// For config-at-end, the end-transform will be reset after the config is actually
// applied in the client (since the transform depends on config). The other properties
// remain here because shell might want to persistently override them.
@@ -1085,9 +1083,8 @@
}
// Need to update layers on involved displays since they were all paused while
// the animation played. This puts the layers back into the correct order.
- for (int i = displays.size() - 1; i >= 0; --i) {
- if (displays.valueAt(i) == null) continue;
- assignLayers(displays.valueAt(i), t);
+ for (int i = participantDisplays.length - 1; i >= 0; --i) {
+ assignLayers(participantDisplays[i], t);
}
for (int i = 0; i < info.getRootCount(); ++i) {
@@ -1800,6 +1797,8 @@
mController.moveToPlaying(this);
// Repopulate the displays based on the resolved targets.
+ final DisplayContent[] participantDisplays = mTargetDisplays.toArray(
+ new DisplayContent[mTargetDisplays.size()]);
mTargetDisplays.clear();
for (int i = 0; i < info.getRootCount(); ++i) {
final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(
@@ -1893,7 +1892,9 @@
controller.setupStartTransaction(transaction);
}
}
- buildFinishTransaction(mFinishTransaction, info);
+ // Use participant displays here (rather than just targets) because it's possible for
+ // there to be order changes between non-top tasks in an otherwise no-op transition.
+ buildFinishTransaction(mFinishTransaction, info, participantDisplays);
mCleanupTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildCleanupTransaction(mCleanupTransaction, info);
if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index af4a48d..886ae7a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -871,6 +871,16 @@
EXEMPT_FROM_POWER_RESTRICTIONS, OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS);
}
+ private static final Set<String> METERED_DATA_RESTRICTION_EXEMPT_ROLES =
+ new ArraySet<>();
+ static {
+ // TODO(b/362545319): reference role name from role manager once it's exposed.
+ final String roleDeviceLockController =
+ "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+ METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(roleDeviceLockController);
+ METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(RoleManager.ROLE_FINANCED_DEVICE_KIOSK);
+ }
+
/**
* Admin apps targeting Android S+ may not use
* {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
@@ -1959,6 +1969,10 @@
return UserManager.isHeadlessSystemUserMode();
}
+ List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) {
+ return getRoleManager().getRoleHoldersAsUser(role, userHandle);
+ }
+
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
@NonNull Intent intent, int flags, Bundle options, UserHandle user) {
@@ -13972,11 +13986,9 @@
UserManager.DISALLOW_THREAD_NETWORK,
new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK});
}
- if (Flags.assistContentUserRestrictionEnabled()) {
- USER_RESTRICTION_PERMISSIONS.put(
- UserManager.DISALLOW_ASSIST_CONTENT,
- new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT});
- }
+ USER_RESTRICTION_PERMISSIONS.put(
+ UserManager.DISALLOW_ASSIST_CONTENT,
+ new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
USER_RESTRICTION_PERMISSIONS.put(
@@ -17902,15 +17914,28 @@
});
}
+ private Set<String> getMeteredDataRestrictionExemptPackages(int userId) {
+ final Set<String> exemptPkgs = new ArraySet<>();
+ for (String role: METERED_DATA_RESTRICTION_EXEMPT_ROLES) {
+ String pkg = getRoleHolderPackageNameOnUser(role, userId);
+ if (pkg != null) {
+ exemptPkgs.add(pkg);
+ }
+ }
+
+ return exemptPkgs;
+ }
+
private List<String> removeInvalidPkgsForMeteredDataRestriction(
int userId, List<String> pkgNames) {
+ final Set<String> exemptRolePkgs = getMeteredDataRestrictionExemptPackages(userId);
synchronized (getLockObject()) {
final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId);
final List<String> excludedPkgs = new ArrayList<>();
for (int i = pkgNames.size() - 1; i >= 0; --i) {
final String pkgName = pkgNames.get(i);
- // If the package is an active admin, don't restrict it.
- if (activeAdmins.contains(pkgName)) {
+ // If the package is an active admin or exempt role, don't restrict it.
+ if (activeAdmins.contains(pkgName) || exemptRolePkgs.contains(pkgName)) {
excludedPkgs.add(pkgName);
continue;
}
@@ -19755,16 +19780,14 @@
}
private void transferSubscriptionOwnership(ComponentName admin, ComponentName target) {
- if (Flags.esimManagementEnabled()) {
- SubscriptionManager subscriptionManager = mContext.getSystemService(
- SubscriptionManager.class);
- for (int subId : getSubscriptionIdsInternal(admin.getPackageName()).toArray()) {
- try {
- subscriptionManager.setGroupOwner(subId, target.getPackageName());
- } catch (Exception e) {
- // Shouldn't happen.
- Slogf.e(LOG_TAG, e, "Error setting group owner for subId: " + subId);
- }
+ SubscriptionManager subscriptionManager = mContext.getSystemService(
+ SubscriptionManager.class);
+ for (int subId : getSubscriptionIdsInternal(admin.getPackageName()).toArray()) {
+ try {
+ subscriptionManager.setGroupOwner(subId, target.getPackageName());
+ } catch (Exception e) {
+ // Shouldn't happen.
+ Slogf.e(LOG_TAG, e, "Error setting group owner for subId: " + subId);
}
}
}
@@ -21753,8 +21776,6 @@
*/
@Nullable
private String getRoleHolderPackageNameOnUser(String role, int userId) {
- RoleManager roleManager = mContext.getSystemService(RoleManager.class);
-
// Clear calling identity as the RoleManager APIs require privileged permissions.
return mInjector.binderWithCleanCallingIdentity(() -> {
List<UserInfo> users;
@@ -21766,7 +21787,7 @@
}
for (UserInfo user : users) {
List<String> roleHolders =
- roleManager.getRoleHoldersAsUser(role, user.getUserHandle());
+ mInjector.roleManagerGetRoleHoldersAsUser(role, user.getUserHandle());
if (!roleHolders.isEmpty()) {
return roleHolders.get(0);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 19a942c..24ee46f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -536,7 +536,6 @@
USER_RESTRICTION_FLAGS.put(
UserManager.DISALLOW_THREAD_NETWORK, POLICY_FLAG_GLOBAL_ONLY_POLICY);
}
- USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ASSIST_CONTENT, /* flags= */ 0);
for (String key : USER_RESTRICTION_FLAGS.keySet()) {
createAndAddUserRestrictionPolicyDefinition(key, USER_RESTRICTION_FLAGS.get(key));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index b4cc343..698bda3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -60,6 +60,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
/**
@@ -325,6 +326,11 @@
}
@Override
+ List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) {
+ return services.roleManagerForMock.getRoleHoldersAsUser(role, userHandle);
+ }
+
+ @Override
PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
Intent intent, int flags, Bundle options, UserHandle user) {
return null;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index b7483d6..cb4269a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -109,6 +109,7 @@
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.WifiSsidPolicy;
+import android.app.role.RoleManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -2889,6 +2890,52 @@
}
@Test
+ public void testSetMeteredDataDisabledPackagesExemptRoles() throws Exception {
+ // TODO(b/362545319): reference role name from role manager once it's exposed.
+ final String controllerRole = "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+
+ setAsProfileOwner(admin1);
+
+ assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEmpty();
+
+ // Setup
+ final ArrayList<String> pkgsToRestrict = new ArrayList<>();
+ final ArrayList<String> pkgsExpectedAsNotRestricted = new ArrayList<>();
+ final String packageWithControllerRole = "com.example.controller";
+ final String packageWithKioskRole = "com.example.kiosk";
+ final String packageWithNotExemptRole = "com.example.notexempt";
+
+ pkgsToRestrict.add(packageWithControllerRole);
+ pkgsToRestrict.add(packageWithKioskRole);
+ pkgsToRestrict.add(packageWithNotExemptRole);
+
+ pkgsExpectedAsNotRestricted.add(packageWithControllerRole);
+ pkgsExpectedAsNotRestricted.add(packageWithKioskRole);
+
+ setupPackageInPackageManager(packageWithControllerRole, CALLER_USER_HANDLE, 123, 0);
+ setupPackageInPackageManager(packageWithKioskRole, CALLER_USER_HANDLE, 456, 0);
+ setupPackageInPackageManager(packageWithNotExemptRole, CALLER_USER_HANDLE, 789, 0);
+
+ when(getServices().roleManagerForMock.getRoleHoldersAsUser(controllerRole,
+ UserHandle.of(CALLER_USER_HANDLE)))
+ .thenReturn(new ArrayList<>(Arrays.asList(packageWithControllerRole)));
+ when(getServices().roleManagerForMock.getRoleHoldersAsUser(
+ RoleManager.ROLE_FINANCED_DEVICE_KIOSK,
+ UserHandle.of(CALLER_USER_HANDLE)))
+ .thenReturn(new ArrayList<>(Arrays.asList(packageWithKioskRole)));
+
+ List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict);
+
+ // Verify
+ assertThat(excludedPkgs).containsExactlyElementsIn(pkgsExpectedAsNotRestricted);
+ assertThat(dpm.getMeteredDataDisabledPackages(admin1))
+ .isEqualTo(Arrays.asList(packageWithNotExemptRole));
+ verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
+ MockUtils.checkApps(packageWithNotExemptRole),
+ eq(CALLER_USER_HANDLE));
+ }
+
+ @Test
public void testSetGetMeteredDataDisabledPackages_deviceAdmin() {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
dpm.setActiveAdmin(admin1, true);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 76aa40c..2e200a9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -142,6 +142,7 @@
public final DevicePolicyManager devicePolicyManager;
public final LocationManager locationManager;
public final RoleManager roleManager;
+ public final RoleManagerForMock roleManagerForMock;
public final SubscriptionManager subscriptionManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -200,6 +201,7 @@
devicePolicyManager = mock(DevicePolicyManager.class);
locationManager = mock(LocationManager.class);
roleManager = realContext.getSystemService(RoleManager.class);
+ roleManagerForMock = mock(RoleManagerForMock.class);
subscriptionManager = mock(SubscriptionManager.class);
// Package manager is huge, so we use a partial mock instead.
@@ -495,6 +497,12 @@
}
}
+ public static class RoleManagerForMock {
+ public List<String> getRoleHoldersAsUser(String role, UserHandle userHandle) {
+ return new ArrayList<>();
+ }
+ }
+
public static class SettingsForMock {
public int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return 0;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 55c48e0..f0a5f75 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -36,7 +36,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,11 +54,6 @@
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- @Before
- public void setUp() {
- mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
- }
-
@Test
public void testNonNull() {
Bundle out = UserRestrictionsUtils.nonNull(null);
@@ -144,7 +138,6 @@
@Test
public void testCanProfileOwnerChange_restrictionRequiresOrgOwnedDevice_orgOwned() {
- mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_SIM_GLOBALLY,
false,
@@ -157,7 +150,6 @@
@Test
public void testCanProfileOwnerChange_restrictionRequiresOrgOwnedDevice_notOrgOwned() {
- mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_SIM_GLOBALLY,
false,
@@ -169,22 +161,7 @@
}
@Test
- public void
- testCanProfileOwnerChange_disabled_restrictionRequiresOrgOwnedDevice_notOrgOwned() {
- mSetFlagsRule.disableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
- assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
- UserManager.DISALLOW_SIM_GLOBALLY,
- false,
- false));
- assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
- UserManager.DISALLOW_SIM_GLOBALLY,
- true,
- false));
- }
-
- @Test
public void testCanProfileOwnerChange_restrictionNotRequiresOrgOwnedDevice_orgOwned() {
- mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADJUST_VOLUME,
false,
@@ -197,7 +174,6 @@
@Test
public void testCanProfileOwnerChange_restrictionNotRequiresOrgOwnedDevice_notOrgOwned() {
- mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADJUST_VOLUME,
false,
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 963b27e..bf58443 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -38,6 +38,7 @@
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -69,6 +70,61 @@
private TunerResourceManagerService mTunerResourceManagerService;
private boolean mIsForeground;
+ private final class TunerClient extends IResourcesReclaimListener.Stub {
+ int[] mClientId;
+ ClientProfile mProfile;
+ boolean mReclaimed;
+
+ TunerClient() {
+ mClientId = new int[1];
+ mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID;
+ }
+
+ public void register(String sessionId, int useCase) {
+ ResourceClientProfile profile = new ResourceClientProfile();
+ profile.tvInputSessionId = sessionId;
+ profile.useCase = useCase;
+ mTunerResourceManagerService.registerClientProfileInternal(
+ profile, this, mClientId);
+ assertThat(mClientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ mProfile = mTunerResourceManagerService.getClientProfile(mClientId[0]);
+ }
+
+ public void register(String sessionId, int useCase, int priority, int niceValue) {
+ register(sessionId, useCase);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ mClientId[0], priority, niceValue);
+ }
+
+ public void register(String sessionId, int useCase, int priority) {
+ register(sessionId, useCase, priority, 0);
+ }
+
+ public void unregister() {
+ mTunerResourceManagerService.unregisterClientProfileInternal(mClientId[0]);
+ mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID;
+ mReclaimed = false;
+ }
+
+ public int getId() {
+ return mClientId[0];
+ }
+
+ public ClientProfile getProfile() {
+ return mProfile;
+ }
+
+ @Override
+ public void onReclaimResources() {
+ mTunerResourceManagerService.clearAllResourcesAndClientMapping(mProfile);
+ mReclaimed = true;
+ }
+
+ public boolean isReclaimed() {
+ return mReclaimed;
+ }
+ }
+
private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub {
boolean mReclaimed;
@@ -247,13 +303,11 @@
}
@Test
- public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
@@ -262,21 +316,20 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
+ client0.unregister();
}
@Test
- public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
@@ -295,27 +348,23 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(0);
+ client0.unregister();
}
@Test
- public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
- ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile0, null /*listener*/, clientId0);
- mTunerResourceManagerService.registerClientProfileInternal(
- profile1, null /*listener*/, clientId1);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
@@ -335,13 +384,13 @@
int[] frontendHandle = new int[1];
TunerFrontendRequest request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
request =
- tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
@@ -349,31 +398,20 @@
.isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].handle).isInUse())
.isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority()
+ throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 50};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 50);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -384,46 +422,36 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isFalse();
- assertThat(listener.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
+ public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority()
+ throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -434,17 +462,16 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList(
- infos[0].handle, infos[1].handle)));
+ assertThat(client0.getProfile().getInUseFrontendHandles())
+ .isEqualTo(new HashSet<Integer>(Arrays.asList(infos[0].handle, infos[1].handle)));
request =
- tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+ tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
@@ -453,22 +480,20 @@
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
- .getOwnerClientId()).isEqualTo(clientId1[0]);
+ .getOwnerClientId()).isEqualTo(client1.getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
- .getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(listener.isReclaimed()).isTrue();
+ .getOwnerClientId()).isEqualTo(client1.getId());
+ assertThat(client0.isReclaimed()).isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void releaseFrontendTest_UnderTheSameExclusiveGroup() {
+ public void releaseFrontendTest_UnderTheSameExclusiveGroup() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -479,7 +504,7 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -488,43 +513,29 @@
.getFrontendResource(infos[1].handle).isInUse()).isTrue();
// Release frontend
- mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
- .getFrontendResource(frontendHandle[0]), clientId[0]);
+ mTunerResourceManagerService.releaseFrontendInternal(frontendHandle[0], client0.getId());
assertThat(mTunerResourceManagerService
.getFrontendResource(frontendHandle[0]).isInUse()).isFalse();
assertThat(mTunerResourceManagerService
.getFrontendResource(infos[1].handle).isInUse()).isFalse();
- assertThat(mTunerResourceManagerService
- .getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0);
+ assertThat(client0.getProfile().getInUseFrontendHandles().size()).isEqualTo(0);
+ client0.unregister();
}
@Test
- public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() {
+ public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/);
+ CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
int[] casSessionHandle = new int[1];
// Request for 2 cas sessions.
assertThat(mTunerResourceManagerService
@@ -533,54 +544,45 @@
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCasSystemId())
+ .isEqualTo(1);
assertThat(mTunerResourceManagerService.getCasResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
- request = casSessionRequest(clientId1[0], 1);
+ request = casSessionRequest(client1.getId(), 1);
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
- .getInUseCasSystemId()).isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(client1.getProfile().getInUseCasSystemId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCasSystemId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
assertThat(mTunerResourceManagerService.getCasResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client1.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
- assertThat(listener.isReclaimed()).isTrue();
+ assertThat(client0.isReclaimed()).isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() {
+ public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority()
+ throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init cicam/cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/);
+ TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
int[] ciCamHandle = new int[1];
// Request for 2 ciCam sessions.
assertThat(mTunerResourceManagerService
@@ -589,139 +591,125 @@
.requestCiCamInternal(request, ciCamHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCiCamId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
assertThat(mTunerResourceManagerService.getCiCamResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
- request = tunerCiCamRequest(clientId1[0], 1);
+ request = tunerCiCamRequest(client1.getId(), 1);
assertThat(mTunerResourceManagerService
.requestCiCamInternal(request, ciCamHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
- .getInUseCiCamId()).isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(client1.getProfile().getInUseCiCamId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCiCamId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
assertThat(mTunerResourceManagerService.getCiCamResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
- assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
- assertThat(listener.isReclaimed()).isTrue();
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client1.getId())));
+ assertThat(mTunerResourceManagerService
+ .getCiCamResource(1).isFullyUsed()).isFalse();
+ assertThat(client0.isReclaimed()).isTrue();
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void releaseCasTest() {
+ public void releaseCasTest() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/);
+ CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
int[] casSessionHandle = new int[1];
// Request for 1 cas sessions.
assertThat(mTunerResourceManagerService
.requestCasSessionInternal(request, casSessionHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCasSystemId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCasSystemId()).isEqualTo(1);
assertThat(mTunerResourceManagerService.getCasResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
// Release cas
mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService
- .getCasResource(1), clientId[0]);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ .getCasResource(1), client0.getId());
+ assertThat(client0.getProfile().getInUseCasSystemId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
assertThat(mTunerResourceManagerService.getCasResource(1)
.getOwnerClientIds()).isEmpty();
+ client0.unregister();
}
@Test
- public void releaseCiCamTest() {
+ public void releaseCiCamTest() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
- TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/);
+ TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
int[] ciCamHandle = new int[1];
// Request for 1 ciCam sessions.
assertThat(mTunerResourceManagerService
.requestCiCamInternal(request, ciCamHandle)).isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
.isEqualTo(1);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCiCamId()).isEqualTo(1);
+ assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
assertThat(mTunerResourceManagerService.getCiCamResource(1)
- .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
- assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+ .getOwnerClientIds()).isEqualTo(
+ new HashSet<Integer>(Arrays.asList(client0.getId())));
+ assertThat(mTunerResourceManagerService
+ .getCiCamResource(1).isFullyUsed()).isFalse();
// Release ciCam
mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService
- .getCiCamResource(1), clientId[0]);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
- .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
- assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+ .getCiCamResource(1), client0.getId());
+ assertThat(client0.getProfile().getInUseCiCamId())
+ .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+ assertThat(mTunerResourceManagerService
+ .getCiCamResource(1).isFullyUsed()).isFalse();
assertThat(mTunerResourceManagerService.getCiCamResource(1)
.getOwnerClientIds()).isEmpty();
+ client0.unregister();
}
@Test
- public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() {
+ public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[2];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- profiles[1] = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientPriorities = {100, 500};
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[0], listener, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId0[0], clientPriorities[0], 0/*niceValue*/);
- mTunerResourceManagerService.registerClientProfileInternal(
- profiles[1], new TestResourcesReclaimListener(), clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.updateClientPriorityInternal(
- clientId1[0], clientPriorities[1], 0/*niceValue*/);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
// Init lnb resources.
int[] lnbHandles = {1};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
TunerLnbRequest request = new TunerLnbRequest();
- request.clientId = clientId0[0];
+ request.clientId = client0.getId();
int[] lnbHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles())
+ assertThat(client0.getProfile().getInUseLnbHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
request = new TunerLnbRequest();
- request.clientId = clientId1[0];
+ request.clientId = client1.getId();
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
@@ -729,29 +717,26 @@
assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
.isInUse()).isTrue();
assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
- .getOwnerClientId()).isEqualTo(clientId1[0]);
- assertThat(listener.isReclaimed()).isTrue();
- assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
- .getInUseLnbHandles().size()).isEqualTo(0);
+ .getOwnerClientId()).isEqualTo(client1.getId());
+ assertThat(client0.isReclaimed()).isTrue();
+ assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void releaseLnbTest() {
+ public void releaseLnbTest() throws RemoteException {
// Register clients
- ResourceClientProfile[] profiles = new ResourceClientProfile[1];
- profiles[0] = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
- mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init lnb resources.
int[] lnbHandles = {0};
mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
TunerLnbRequest request = new TunerLnbRequest();
- request.clientId = clientId[0];
+ request.clientId = client0.getId();
int[] lnbHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestLnbInternal(request, lnbHandle)).isTrue();
@@ -762,19 +747,16 @@
.getLnbResource(lnbHandle[0]));
assertThat(mTunerResourceManagerService
.getLnbResource(lnbHandle[0]).isInUse()).isFalse();
- assertThat(mTunerResourceManagerService
- .getClientProfile(clientId[0]).getInUseLnbHandles().size()).isEqualTo(0);
+ assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+ client0.unregister();
}
@Test
- public void unregisterClientTest_usingFrontend() {
- // Register client
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void unregisterClientTest_usingFrontend() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -785,7 +767,7 @@
mTunerResourceManagerService.setFrontendInfoListInternal(infos);
TunerFrontendRequest request =
- tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+ tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
int[] frontendHandle = new int[1];
assertThat(mTunerResourceManagerService
.requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -796,26 +778,20 @@
.isInUse()).isTrue();
// Unregister client when using frontend
- mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
+ client0.unregister();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isFalse();
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
- assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
-
+ assertThat(mTunerResourceManagerService.checkClientExists(client0.getId())).isFalse();
}
@Test
- public void requestDemuxTest() {
- // Register client
- ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId0 = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile0, null /*listener*/, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ public void requestDemuxTest() throws RemoteException {
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
TunerDemuxInfo[] infos = new TunerDemuxInfo[3];
infos[0] = tunerDemuxInfo(0 /* handle */, Filter.TYPE_TS | Filter.TYPE_IP);
@@ -825,7 +801,7 @@
int[] demuxHandle0 = new int[1];
// first with undefined type (should be the first one with least # of caps)
- TunerDemuxRequest request = tunerDemuxRequest(clientId0[0], Filter.TYPE_UNDEFINED);
+ TunerDemuxRequest request = tunerDemuxRequest(client0.getId(), Filter.TYPE_UNDEFINED);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
.isTrue();
assertThat(demuxHandle0[0]).isEqualTo(1);
@@ -846,16 +822,16 @@
assertThat(demuxHandle0[0]).isEqualTo(2);
// request for another TS
- int[] clientId1 = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile1, null /*listener*/, clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client1 = new TunerClient();
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+
int[] demuxHandle1 = new int[1];
- TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_TS);
+ TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_TS);
assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1))
.isTrue();
assertThat(demuxHandle1[0]).isEqualTo(0);
- assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle1[0]))
+ assertThat(mTunerResourceManagerService.getResourceIdFromHandle(client1.getId()))
.isEqualTo(0);
// release demuxes
@@ -863,33 +839,23 @@
mTunerResourceManagerService.releaseDemuxInternal(dr);
dr = mTunerResourceManagerService.getDemuxResource(demuxHandle1[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
+
+ client0.unregister();
+ client1.unregister();
}
@Test
- public void requestDemuxTest_ResourceReclaim() {
+ public void requestDemuxTest_ResourceReclaim() throws RemoteException {
// Register clients
- ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
- ResourceClientProfile profile2 = resourceClientProfile("2" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
- int[] clientId0 = new int[1];
- int[] clientId1 = new int[1];
- int[] clientId2 = new int[1];
- TestResourcesReclaimListener listener0 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener listener1 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener listener2 = new TestResourcesReclaimListener();
-
- mTunerResourceManagerService.registerClientProfileInternal(
- profile0, listener0, clientId0);
- assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.registerClientProfileInternal(
- profile1, listener1, clientId1);
- assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.registerClientProfileInternal(
- profile2, listener2, clientId1);
- assertThat(clientId2[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient client0 = new TunerClient();
+ TunerClient client1 = new TunerClient();
+ TunerClient client2 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+ client1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
+ client2.register("2" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
// Init demux resources.
TunerDemuxInfo[] infos = new TunerDemuxInfo[2];
@@ -897,66 +863,67 @@
infos[1] = tunerDemuxInfo(1 /*handle*/, Filter.TYPE_TS);
mTunerResourceManagerService.setDemuxInfoListInternal(infos);
- // let clientId0(prio:100) request for IP - should succeed
- TunerDemuxRequest request0 = tunerDemuxRequest(clientId0[0], Filter.TYPE_IP);
+ // let client0(prio:100) request for IP - should succeed
+ TunerDemuxRequest request0 = tunerDemuxRequest(client0.getId(), Filter.TYPE_IP);
int[] demuxHandle0 = new int[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
assertThat(demuxHandle0[0]).isEqualTo(0);
- // let clientId1(prio:50) request for IP - should fail
- TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_IP);
+ // let client1(prio:50) request for IP - should fail
+ TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_IP);
int[] demuxHandle1 = new int[1];
demuxHandle1[0] = -1;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isFalse();
- assertThat(listener0.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
assertThat(demuxHandle1[0]).isEqualTo(-1);
- // let clientId1(prio:50) request for TS - should succeed
+ // let client1(prio:50) request for TS - should succeed
request1.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request1, demuxHandle1)).isTrue();
assertThat(demuxHandle1[0]).isEqualTo(1);
- assertThat(listener0.isReclaimed()).isFalse();
+ assertThat(client0.isReclaimed()).isFalse();
- // now release demux for the clientId0 (higher priority) and request demux
+ // now release demux for the client0 (higher priority) and request demux
DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
mTunerResourceManagerService.releaseDemuxInternal(dr);
- // let clientId2(prio:50) request for TS - should succeed
- TunerDemuxRequest request2 = tunerDemuxRequest(clientId2[0], Filter.TYPE_TS);
+ // let client2(prio:50) request for TS - should succeed
+ TunerDemuxRequest request2 = tunerDemuxRequest(client2.getId(), Filter.TYPE_TS);
int[] demuxHandle2 = new int[1];
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request2, demuxHandle2)).isTrue();
assertThat(demuxHandle2[0]).isEqualTo(0);
- assertThat(listener1.isReclaimed()).isFalse();
+ assertThat(client1.isReclaimed()).isFalse();
- // let clientId0(prio:100) request for TS - should reclaim from clientId2
+ // let client0(prio:100) request for TS - should reclaim from client1
// , who has the smaller caps
request0.desiredFilterTypes = Filter.TYPE_TS;
assertThat(mTunerResourceManagerService
.requestDemuxInternal(request0, demuxHandle0)).isTrue();
- assertThat(listener1.isReclaimed()).isFalse();
- assertThat(listener2.isReclaimed()).isTrue();
+ assertThat(client1.isReclaimed()).isTrue();
+ assertThat(client2.isReclaimed()).isFalse();
+ client0.unregister();
+ client1.unregister();
+ client2.unregister();
}
@Test
public void requestDescramblerTest() {
- // Register client
- ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
- int[] clientId = new int[1];
- mTunerResourceManagerService.registerClientProfileInternal(
- profile, null /*listener*/, clientId);
- assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ // Register clients
+ TunerClient client0 = new TunerClient();
+ client0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
int[] desHandle = new int[1];
TunerDescramblerRequest request = new TunerDescramblerRequest();
- request.clientId = clientId[0];
+ request.clientId = client0.getId();
assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
.isTrue();
assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+ client0.unregister();
}
@Test
@@ -978,74 +945,26 @@
}
@Test
- public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() {
+ public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() throws RemoteException {
/**** Register Clients and Set Priority ****/
-
- // Int array to save the returned client ids
- int[] ownerClientId0 = new int[1];
- int[] ownerClientId1 = new int[1];
- int[] shareClientId0 = new int[1];
- int[] shareClientId1 = new int[1];
-
- // Predefined client profiles
- ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2];
- ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2];
- ownerProfiles[0] = resourceClientProfile(
- "0" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
- ownerProfiles[1] = resourceClientProfile(
- "1" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
- shareProfiles[0] = resourceClientProfile(
- "2" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
- shareProfiles[1] = resourceClientProfile(
- "3" /*sessionId*/,
- TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
-
- // Predefined client reclaim listeners
- TestResourcesReclaimListener ownerListener0 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener shareListener0 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener ownerListener1 = new TestResourcesReclaimListener();
- TestResourcesReclaimListener shareListener1 = new TestResourcesReclaimListener();
- // Register clients and validate the returned client ids
- mTunerResourceManagerService
- .registerClientProfileInternal(ownerProfiles[0], ownerListener0, ownerClientId0);
- mTunerResourceManagerService
- .registerClientProfileInternal(shareProfiles[0], shareListener0, shareClientId0);
- mTunerResourceManagerService
- .registerClientProfileInternal(ownerProfiles[1], ownerListener1, ownerClientId1);
- mTunerResourceManagerService
- .registerClientProfileInternal(shareProfiles[1], shareListener1, shareClientId1);
- assertThat(ownerClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(shareClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(ownerClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- assertThat(shareClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+ TunerClient ownerClient0 = new TunerClient();
+ TunerClient ownerClient1 = new TunerClient();
+ TunerClient shareClient0 = new TunerClient();
+ TunerClient shareClient1 = new TunerClient();
+ ownerClient0.register("0" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 100);
+ ownerClient1.register("1" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 300);
+ shareClient0.register("2" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 200);
+ shareClient1.register("3" /*sessionId*/,
+ TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 400);
mTunerResourceManagerService.updateClientPriorityInternal(
- ownerClientId0[0],
- 100/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- shareClientId0[0],
- 200/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- ownerClientId1[0],
- 300/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- shareClientId1[0],
- 400/*priority*/,
- 0/*niceValue*/);
- mTunerResourceManagerService.updateClientPriorityInternal(
- shareClientId1[0],
+ shareClient1.getId(),
-1/*invalid priority*/,
0/*niceValue*/);
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId1[0])
- .getPriority())
- .isEqualTo(400);
+ assertThat(shareClient1.getProfile().getPriority()).isEqualTo(400);
/**** Init Frontend Resources ****/
@@ -1072,7 +991,7 @@
// Predefined frontend request and array to save returned frontend handle
int[] frontendHandle = new int[1];
TunerFrontendRequest request = tunerFrontendRequest(
- ownerClientId0[0] /*clientId*/,
+ ownerClient0.getId() /*clientId*/,
FrontendSettings.TYPE_DVBT);
// Request call and validate granted resource and internal mapping
@@ -1080,9 +999,7 @@
.requestFrontendInternal(request, frontendHandle))
.isTrue();
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
@@ -1091,11 +1008,11 @@
// Share frontend call and validate the internal mapping
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId0[0]/*selfClientId*/,
- ownerClientId0[0]/*targetClientId*/);
+ shareClient0.getId()/*selfClientId*/,
+ ownerClient0.getId()/*targetClientId*/);
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId1[0]/*selfClientId*/,
- ownerClientId0[0]/*targetClientId*/);
+ shareClient1.getId()/*selfClientId*/,
+ ownerClient0.getId()/*targetClientId*/);
// Verify fe in use status
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
.isInUse()).isTrue();
@@ -1103,31 +1020,24 @@
.isInUse()).isTrue();
// Verify fe owner status
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+ .getOwnerClientId()).isEqualTo(ownerClient0.getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+ .getOwnerClientId()).isEqualTo(ownerClient0.getId());
// Verify share fe client status in the primary owner client
- assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
- .getShareFeClientIds())
+ assertThat(ownerClient0.getProfile().getShareFeClientIds())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- shareClientId0[0],
- shareClientId1[0])));
+ shareClient0.getId(),
+ shareClient1.getId())));
// Verify in use frontend list in all the primary owner and share owner clients
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles())
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId1[0])
- .getInUseFrontendHandles())
+ assertThat(shareClient1.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
@@ -1135,21 +1045,17 @@
/**** Remove Frontend Share Owner ****/
// Unregister the second share fe client
- mTunerResourceManagerService.unregisterClientProfileInternal(shareClientId1[0]);
+ shareClient1.unregister();
// Validate the internal mapping
- assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
- .getShareFeClientIds())
+ assertThat(ownerClient0.getProfile().getShareFeClientIds())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
- shareClientId0[0])));
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles())
+ shareClient0.getId())));
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
+ assertThat(shareClient0.getProfile()
.getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
@@ -1159,7 +1065,7 @@
// Predefined second frontend request
request = tunerFrontendRequest(
- ownerClientId1[0] /*clientId*/,
+ ownerClient1.getId() /*clientId*/,
FrontendSettings.TYPE_DVBT);
// Second request call
@@ -1170,43 +1076,35 @@
// Validate granted resource and internal mapping
assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
+ .getOwnerClientId()).isEqualTo(ownerClient1.getId());
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
- .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getInUseFrontendHandles())
+ .getOwnerClientId()).isEqualTo(ownerClient1.getId());
+ assertThat(ownerClient1.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
infos[0].handle,
infos[1].handle)));
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(ownerClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId0[0])
- .getShareFeClientIds()
+ assertThat(ownerClient0.getProfile().getShareFeClientIds()
.isEmpty())
.isTrue();
- assertThat(ownerListener0.isReclaimed()).isTrue();
- assertThat(shareListener0.isReclaimed()).isTrue();
+ assertThat(ownerClient0.isReclaimed()).isTrue();
+ assertThat(shareClient0.isReclaimed()).isTrue();
/**** Release Frontend Resource From Primary Owner ****/
// Reshare the frontend
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId0[0]/*selfClientId*/,
- ownerClientId1[0]/*targetClientId*/);
+ shareClient0.getId()/*selfClientId*/,
+ ownerClient1.getId()/*targetClientId*/);
// Release the frontend resource from the primary owner
- mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
- .getFrontendResource(infos[0].handle), ownerClientId1[0]);
+ mTunerResourceManagerService.releaseFrontendInternal(infos[0].handle,
+ ownerClient1.getId());
// Validate the internal mapping
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
@@ -1214,19 +1112,13 @@
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
// Verify client status
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getInUseFrontendHandles()
+ assertThat(ownerClient1.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(ownerClientId1[0])
- .getShareFeClientIds()
+ assertThat(ownerClient1.getProfile().getShareFeClientIds()
.isEmpty())
.isTrue();
@@ -1234,7 +1126,7 @@
// Predefined Lnb request and handle array
TunerLnbRequest requestLnb = new TunerLnbRequest();
- requestLnb.clientId = shareClientId0[0];
+ requestLnb.clientId = shareClient0.getId();
int[] lnbHandle = new int[1];
// Request for an Lnb
@@ -1247,11 +1139,11 @@
.requestFrontendInternal(request, frontendHandle))
.isTrue();
mTunerResourceManagerService.shareFrontendInternal(
- shareClientId0[0]/*selfClientId*/,
- ownerClientId1[0]/*targetClientId*/);
+ shareClient0.getId()/*selfClientId*/,
+ ownerClient1.getId()/*targetClientId*/);
// Unregister the primary owner of the shared frontend
- mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]);
+ ownerClient1.unregister();
// Validate the internal mapping
assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
@@ -1259,16 +1151,15 @@
assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
.isInUse()).isFalse();
// Verify client status
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseFrontendHandles()
+ assertThat(shareClient0.getProfile().getInUseFrontendHandles()
.isEmpty())
.isTrue();
- assertThat(mTunerResourceManagerService
- .getClientProfile(shareClientId0[0])
- .getInUseLnbHandles())
+ assertThat(shareClient0.getProfile().getInUseLnbHandles())
.isEqualTo(new HashSet<Integer>(Arrays.asList(
lnbHandles[0])));
+
+ ownerClient0.unregister();
+ shareClient0.unregister();
}
private TunerFrontendInfo tunerFrontendInfo(
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index f3ecfcc..66788b6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -45,6 +45,8 @@
import androidx.test.InstrumentationRegistry;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -262,7 +264,7 @@
public void vibrateIfAvailable_withNoInputDevice_returnsFalse() {
assertFalse(mInputDeviceDelegate.isAvailable());
assertFalse(mInputDeviceDelegate.vibrateIfAvailable(
- new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
+ new CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
SYNCED_EFFECT));
}
@@ -277,7 +279,7 @@
mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
assertTrue(mInputDeviceDelegate.vibrateIfAvailable(
- new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
+ new CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
SYNCED_EFFECT));
verify(mIInputManagerMock).vibrateCombined(eq(1), same(SYNCED_EFFECT), any());
verify(mIInputManagerMock).vibrateCombined(eq(2), same(SYNCED_EFFECT), any());
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java
similarity index 80%
rename from services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java
rename to services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java
index 84f8412..f69d1c4 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -24,13 +24,13 @@
import java.util.Arrays;
-public class VibrationTest {
+public class VibrationSessionTest {
@Test
public void status_hasUniqueProtoEnumValues() {
assertThat(
- Arrays.stream(Vibration.Status.values())
- .map(Vibration.Status::getProtoEnumValue)
+ Arrays.stream(VibrationSession.Status.values())
+ .map(VibrationSession.Status::getProtoEnumValue)
.collect(toList()))
.containsNoDuplicates();
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 38cd49d..c7a136a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -81,6 +81,8 @@
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.Status;
import org.junit.After;
import org.junit.Before;
@@ -292,7 +294,7 @@
if (expectedAllowedVibrations.contains(usage)) {
assertVibrationNotIgnoredForUsage(usage);
} else {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_BACKGROUND);
}
}
}
@@ -350,7 +352,7 @@
createSystemReadyVibrationSettings();
for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
}
}
@@ -365,7 +367,7 @@
mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, wirelessChargingIntent);
for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
}
}
@@ -377,7 +379,7 @@
createSystemReadyVibrationSettings();
// Check that initially, all usages are ignored due to the wireless charging.
for (int usage : ALL_USAGES) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
}
Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
@@ -404,7 +406,7 @@
if (expectedAllowedVibrations.contains(usage)) {
assertVibrationNotIgnoredForUsage(usage);
} else {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_POWER);
}
}
}
@@ -426,7 +428,7 @@
for (int usage : ALL_USAGES) {
if (usage == USAGE_RINGTONE || usage == USAGE_NOTIFICATION) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_RINGER_MODE);
} else {
assertVibrationNotIgnoredForUsage(usage);
}
@@ -470,7 +472,7 @@
if (usage == USAGE_ACCESSIBILITY) {
assertVibrationNotIgnoredForUsage(usage);
} else {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
}
assertVibrationNotIgnoredForUsageAndFlags(usage,
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
@@ -512,7 +514,7 @@
for (int usage : ALL_USAGES) {
if (usage == USAGE_TOUCH) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
}
@@ -527,7 +529,7 @@
for (int usage : ALL_USAGES) {
if (usage == USAGE_TOUCH || usage == USAGE_IME_FEEDBACK) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
}
@@ -542,7 +544,7 @@
for (int usage : ALL_USAGES) {
if (usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
}
@@ -557,7 +559,7 @@
for (int usage : ALL_USAGES) {
if (usage == USAGE_NOTIFICATION) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
}
@@ -574,7 +576,7 @@
for (int usage : ALL_USAGES) {
if (usage == USAGE_RINGTONE) {
- assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
} else {
assertVibrationNotIgnoredForUsage(usage);
}
@@ -597,7 +599,7 @@
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
- assertVibrationIgnoredForUsage(USAGE_RINGTONE, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+ assertVibrationIgnoredForUsage(USAGE_RINGTONE, Status.IGNORED_FOR_RINGER_MODE);
}
@Test
@@ -611,7 +613,7 @@
new VibrationAttributes.Builder()
.setUsage(USAGE_IME_FEEDBACK)
.build(),
- Vibration.Status.IGNORED_FOR_SETTINGS);
+ Status.IGNORED_FOR_SETTINGS);
// General touch and keyboard touch with bypass flag not ignored.
assertVibrationNotIgnoredForUsage(USAGE_TOUCH);
@@ -629,7 +631,7 @@
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
// General touch ignored.
- assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(USAGE_TOUCH, Status.IGNORED_FOR_SETTINGS);
// Keyboard touch not ignored.
assertVibrationNotIgnoredForAttributes(
@@ -645,14 +647,14 @@
setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
// General touch ignored.
- assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+ assertVibrationIgnoredForUsage(USAGE_TOUCH, Status.IGNORED_FOR_SETTINGS);
// Keyboard touch ignored.
assertVibrationIgnoredForAttributes(
new VibrationAttributes.Builder()
.setUsage(USAGE_IME_FEEDBACK)
.build(),
- Vibration.Status.IGNORED_FOR_SETTINGS);
+ Status.IGNORED_FOR_SETTINGS);
}
@Test
@@ -668,7 +670,7 @@
// Ignore the vibration when the coming device id represents a virtual device.
for (int usage : ALL_USAGES) {
assertVibrationIgnoredForUsageAndDevice(usage, VIRTUAL_DEVICE_ID,
- Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
+ Status.IGNORED_FROM_VIRTUAL_DEVICE);
}
}
@@ -911,22 +913,21 @@
}
private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
- Vibration.Status expectedStatus) {
+ Status expectedStatus) {
assertVibrationIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT, expectedStatus);
}
private void assertVibrationIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
- int deviceId, Vibration.Status expectedStatus) {
- Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+ int deviceId, Status expectedStatus) {
+ CallerInfo callerInfo = new CallerInfo(
VibrationAttributes.createForUsage(usage), UID, deviceId, null, null);
assertEquals(errorMessageForUsage(usage), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
- Vibration.Status expectedStatus) {
- Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
- Context.DEVICE_ID_DEFAULT, null, null);
+ Status expectedStatus) {
+ CallerInfo callerInfo = new CallerInfo(attrs, UID, Context.DEVICE_ID_DEFAULT, null, null);
assertEquals(errorMessageForAttributes(attrs), expectedStatus,
mVibrationSettings.shouldIgnoreVibration(callerInfo));
}
@@ -948,7 +949,7 @@
private void assertVibrationNotIgnoredForUsageAndFlagsAndDevice(
@VibrationAttributes.Usage int usage, int deviceId,
@VibrationAttributes.Flag int flags) {
- Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+ CallerInfo callerInfo = new CallerInfo(
new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID,
deviceId, null, null);
assertNull(errorMessageForUsage(usage),
@@ -956,7 +957,7 @@
}
private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
- Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
+ CallerInfo callerInfo = new CallerInfo(attrs, UID,
Context.DEVICE_ID_DEFAULT, null, null);
assertNull(errorMessageForAttributes(attrs),
mVibrationSettings.shouldIgnoreVibration(callerInfo));
@@ -1017,10 +1018,10 @@
new PowerManager.SleepData(sleepTime, reason));
}
- private Vibration.CallerInfo createCallerInfo(int uid, String opPkg,
+ private CallerInfo createCallerInfo(int uid, String opPkg,
@VibrationAttributes.Usage int usage) {
VibrationAttributes attrs = VibrationAttributes.createForUsage(usage);
- return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
+ return new CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
}
private void setBatteryReceiverRegistrationResult(Intent result) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index bfdaa78..31cc50f1 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -78,6 +78,8 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.Status;
import org.junit.After;
import org.junit.Before;
@@ -189,7 +191,7 @@
waitForCompletion();
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+ verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
}
@Test
@@ -202,7 +204,7 @@
waitForCompletion();
verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+ verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
}
@Test
@@ -216,7 +218,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
@@ -233,7 +235,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
@@ -253,7 +255,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(15)),
@@ -326,13 +328,10 @@
assertTrue(mThread.isRunningVibrationId(vibrationId));
assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
- Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
- Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo(
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */
- 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
- mVibrationConductor.notifyCancelled(
- cancelVibrationInfo,
- /* immediate= */ false);
+ Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
+ new CallerInfo(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM),
+ /* uid= */ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
+ mVibrationConductor.notifyCancelled(cancelVibrationInfo, /* immediate= */ false);
waitForCompletion();
assertFalse(mThread.isRunningVibrationId(vibrationId));
@@ -363,11 +362,10 @@
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(5000)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -385,7 +383,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(300L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
@@ -406,7 +404,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(350L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
@@ -433,11 +431,10 @@
assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() >= 5,
5000L + TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).subList(0, 5))
@@ -466,12 +463,11 @@
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
// PWLE size max was used to generate a single vibrate call with 10 segments.
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
}
@@ -496,12 +492,11 @@
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false);
waitForCompletion();
// Composition size max was used to generate a single vibrate call with 10 primitives.
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
}
@@ -519,11 +514,10 @@
assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(5550)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -545,11 +539,10 @@
assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
expectedOnDuration + TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibrationId);
// First time, turn vibrator ON for the expected fixed duration.
@@ -584,14 +577,14 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
new Thread(() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE),
/* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -614,14 +607,14 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
new Thread(() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE),
/* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -641,14 +634,14 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread =
new Thread(() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
/* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -663,7 +656,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
@@ -684,7 +677,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedOneShot(10)),
@@ -701,7 +694,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+ verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
}
@@ -718,7 +711,7 @@
eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
@@ -743,7 +736,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
@@ -762,7 +755,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+ verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
}
@@ -784,7 +777,7 @@
long vibrationId = startThreadAndDispatcher(effect);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
// Vibrator compose called twice.
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
assertEquals(3, fakeVibrator.getEffectSegments(vibrationId).size());
@@ -824,7 +817,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedOneShot(10),
@@ -865,7 +858,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
List<VibrationEffectSegment> segments =
@@ -904,7 +897,7 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
@@ -942,7 +935,7 @@
long vibrationId = startThreadAndDispatcher(effect);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
// Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
// Using best split points instead of max-packing PWLEs.
@@ -967,7 +960,7 @@
waitForCompletion();
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED);
}
@Test
@@ -977,7 +970,7 @@
long vibrationId = startThreadAndDispatcher(VibrationEffect.createOneShot(10, 100));
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any());
verify(mManagerHooks, never()).triggerSyncedVibration(anyLong());
verify(mManagerHooks, never()).cancelSyncedVibration();
@@ -998,7 +991,7 @@
verify(mManagerHooks).noteVibratorOff(eq(UID));
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
@@ -1022,7 +1015,7 @@
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
assertFalse(mControllers.get(3).isVibrating());
@@ -1065,7 +1058,7 @@
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
assertFalse(mControllers.get(3).isVibrating());
@@ -1117,7 +1110,7 @@
batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
assertFalse(mControllers.get(3).isVibrating());
@@ -1166,7 +1159,7 @@
verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
verify(mManagerHooks, never()).cancelSyncedVibration();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
VibrationEffectSegment expected = expectedPrimitive(
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
@@ -1213,7 +1206,7 @@
verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
verify(mManagerHooks, never()).cancelSyncedVibration();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
}
@Test
@@ -1305,7 +1298,7 @@
verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
assertFalse(mControllers.get(3).isVibrating());
@@ -1478,8 +1471,7 @@
// fail at waitForCompletion(cancellingThread).
Thread cancellingThread = new Thread(
() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false));
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false));
cancellingThread.start();
// Cancelling the vibration should be fast and return right away, even if the thread is
@@ -1488,7 +1480,7 @@
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS);
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -1517,14 +1509,14 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread = new Thread(
() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
/* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1551,14 +1543,14 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread = new Thread(
() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
/* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1585,15 +1577,14 @@
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
Thread cancellingThread = new Thread(
() -> mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(
- Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+ new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
/* immediate= */ false));
cancellingThread.start();
waitForCompletion(/* timeout= */ 50);
cancellingThread.join();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
}
@@ -1612,7 +1603,7 @@
verify(mVibrationToken).linkToDeath(same(mVibrationConductor), eq(0));
verify(mVibrationToken).unlinkToDeath(same(mVibrationConductor), eq(0));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED);
assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
}
@@ -1628,7 +1619,7 @@
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
// Duration extended for 5 + 5 + 5 + 15.
assertEquals(Arrays.asList(expectedOneShot(30)),
@@ -1651,7 +1642,7 @@
// Vibration completed but vibrator not yet released.
verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
- eq(new Vibration.EndInfo(Vibration.Status.FINISHED)));
+ eq(new Vibration.EndInfo(Status.FINISHED)));
verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong());
// Thread still running ramp down.
@@ -1663,13 +1654,12 @@
// Will stop the ramp down right away.
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
- /* immediate= */ true);
+ new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ true);
waitForCompletion();
// Does not cancel already finished vibration, but releases vibrator.
verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
- eq(new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE)));
+ eq(new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE)));
verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
}
@@ -1684,11 +1674,10 @@
assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
TEST_TIMEOUT_MILLIS));
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
- verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
// Duration extended for 10000 + 15.
assertEquals(Arrays.asList(expectedOneShot(10_015)),
@@ -1711,7 +1700,7 @@
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
@@ -1729,7 +1718,7 @@
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
.containsExactly(effect)
@@ -1752,7 +1741,7 @@
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertEquals(
Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
@@ -1779,7 +1768,7 @@
waitForCompletion();
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
- verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId, Status.FINISHED);
assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
fakeVibrator.getEffectSegments(vibrationId));
@@ -1810,14 +1799,13 @@
long vibrationId1 = startThreadAndDispatcher(effect1);
waitForCompletion();
verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
- verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId1, Status.FINISHED);
long vibrationId2 = startThreadAndDispatcher(effect2);
// Effect2 won't complete on its own. Cancel it after a couple of repeats.
Thread.sleep(150); // More than two TICKs.
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
- /* immediate= */ false);
+ new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
long vibrationId3 = startThreadAndDispatcher(effect3);
@@ -1827,8 +1815,7 @@
long start4 = System.currentTimeMillis();
long vibrationId4 = startThreadAndDispatcher(effect4);
mVibrationConductor.notifyCancelled(
- new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
- /* immediate= */ true);
+ new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ true);
waitForCompletion();
long duration4 = System.currentTimeMillis() - start4;
@@ -1841,14 +1828,14 @@
// Effect1
verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
- verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId1, Status.FINISHED);
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
fakeVibrator.getEffectSegments(vibrationId1));
// Effect2: repeating, cancelled.
verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2);
- verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED_BY_USER);
+ verifyCallbacksTriggered(vibrationId2, Status.CANCELLED_BY_USER);
// The exact count of segments might vary, so just check that there's more than 2 and
// all elements are the same segment.
@@ -1860,13 +1847,13 @@
// Effect3
verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId3));
- verifyCallbacksTriggered(vibrationId3, Vibration.Status.FINISHED);
+ verifyCallbacksTriggered(vibrationId3, Status.FINISHED);
assertEquals(Arrays.asList(
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
fakeVibrator.getEffectSegments(vibrationId3));
// Effect4: cancelled quickly.
- verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+ verifyCallbacksTriggered(vibrationId4, Status.CANCELLED_BY_SCREEN_OFF);
assertTrue("Tested duration=" + duration4, duration4 < 2000);
// Effect5: played normally after effect4, which may or may not have played.
@@ -1907,7 +1894,7 @@
.build();
HalVibration vib = new HalVibration(mVibrationToken,
CombinedVibration.createParallel(effect),
- new Vibration.CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
+ new CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
return startThreadAndDispatcher(vib, requestVibrationParamsFuture);
}
@@ -1944,7 +1931,7 @@
private HalVibration createVibration(CombinedVibration effect) {
return new HalVibration(mVibrationToken, effect,
- new Vibration.CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
+ new CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
}
private SparseArray<VibratorController> createVibratorControllers() {
@@ -2007,7 +1994,7 @@
.collect(Collectors.toList());
}
- private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
+ private void verifyCallbacksTriggered(long vibrationId, Status expectedStatus) {
verifyCallbacksTriggered(vibrationId, new Vibration.EndInfo(expectedStatus));
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
index 3466bbb..cd057b6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
@@ -22,6 +22,8 @@
import android.os.Handler;
import android.os.test.TestLooper;
+import com.android.server.vibrator.VibrationSession.Status;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -113,7 +115,6 @@
}
private static VibrationStats.StatsInfo newEmptyStatsInfo() {
- return new VibrationStats.StatsInfo(
- 0, 0, 0, Vibration.Status.FINISHED, new VibrationStats(), 0L);
+ return new VibrationStats.StatsInfo(0, 0, 0, Status.FINISHED, new VibrationStats(), 0L);
}
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 4afb562..4012575 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -110,6 +110,7 @@
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.BackgroundUserSoundNotifier;
+import com.android.server.vibrator.VibrationSession.Status;
import org.junit.After;
import org.junit.Before;
@@ -803,8 +804,8 @@
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- var vib = vibrate(service,
- VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), RINGTONE_ATTRS);
+ HalVibration vib = vibrate(service, VibrationEffect.createWaveform(
+ new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), RINGTONE_ATTRS);
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -815,14 +816,80 @@
service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null);
assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+ assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_APP_OPS);
+ }
- var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
- verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
- .writeVibrationReportedAsync(statsInfoCaptor.capture());
+ @Test
+ public void vibrate_thenPowerModeChanges_getsCancelled() throws Exception {
+ mockVibrators(1, 2);
+ VibratorManagerService service = createSystemReadyService();
- VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0);
- assertEquals(Vibration.Status.CANCELLED_BY_APP_OPS.getProtoEnumValue(),
- touchMetrics.status);
+ HalVibration vib = vibrate(service,
+ CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100))
+ .combine(),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+ assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE);
+ }
+
+ @Test
+ public void vibrate_thenSettingsRefreshedWithoutChange_doNotCancelVibration() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ vibrate(service, VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.updateServiceState();
+
+ // Vibration is not stopped nearly after updating service.
+ assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
+ }
+
+ @Test
+ public void vibrate_thenSettingsChange_getsCancelled() throws Exception {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ HalVibration vib = vibrate(service,
+ VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100),
+ HAPTIC_FEEDBACK_ATTRS);
+
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+ service.mVibrationSettings.mSettingObserver.onChange(false);
+ service.updateServiceState();
+
+ assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+ assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE);
+ }
+
+ @Test
+ public void vibrate_thenScreenTurnsOff_getsCancelled() throws Throwable {
+ mockVibrators(1);
+ VibratorManagerService service = createSystemReadyService();
+
+ HalVibration vib = vibrate(service, VibrationEffect.createWaveform(
+ new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), ALARM_ATTRS);
+
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF));
+
+ assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+ assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SCREEN_OFF);
}
@Test
@@ -831,8 +898,8 @@
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- var vib = vibrate(service,
- VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), ALARM_ATTRS);
+ HalVibration vib = vibrate(service, VibrationEffect.createWaveform(
+ new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), ALARM_ATTRS);
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -841,14 +908,7 @@
BackgroundUserSoundNotifier.ACTION_MUTE_SOUND));
assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
-
- var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
- verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
- .writeVibrationReportedAsync(statsInfoCaptor.capture());
-
- VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0);
- assertEquals(Vibration.Status.CANCELLED_BY_FOREGROUND_USER.getProtoEnumValue(),
- touchMetrics.status);
+ assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_FOREGROUND_USER);
}
@Test
@@ -1314,7 +1374,7 @@
}
@Test
- public void vibrate_withriggerCallback_finishesVibration() throws Exception {
+ public void vibrate_withTriggerCallback_finishesVibration() throws Exception {
mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1947,41 +2007,6 @@
}
@Test
- public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
- mockVibrators(1, 2);
- VibratorManagerService service = createSystemReadyService();
- vibrate(service,
- CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
- .combine(),
- HAPTIC_FEEDBACK_ATTRS);
-
- // VibrationThread will start this vibration async, so wait until vibration is triggered.
- assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
- // Haptic feedback cancelled on low power mode.
- assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- }
-
- @Test
- public void vibrate_withSettingsChange_doNotCancelVibration() throws Exception {
- mockVibrators(1);
- VibratorManagerService service = createSystemReadyService();
-
- vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
- // VibrationThread will start this vibration async, so wait until vibration is triggered.
- assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-
- service.updateServiceState();
-
- // Vibration is not stopped nearly after updating service.
- assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
- }
-
- @Test
public void vibrate_ignoreVibrationFromVirtualDevice() throws Exception {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -2475,6 +2500,113 @@
}
@Test
+ public void onExternalVibration_thenDeniedAppOps_doNotCancelVibration() throws Throwable {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ when(mAppOpsManagerMock.checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+ eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+ service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null);
+
+ verify(externalVibrationControllerMock, never()).mute();
+ }
+
+ @Test
+ public void onExternalVibration_thenPowerModeChanges_doNotCancelVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ verify(externalVibrationControllerMock, never()).mute();
+ }
+
+ @Test
+ public void onExternalVibration_thenSettingsChange_doNotCancelVibration() throws Exception {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+ service.mVibrationSettings.mSettingObserver.onChange(false);
+ service.updateServiceState();
+
+ verify(externalVibrationControllerMock, never()).mute();
+ }
+
+ @Test
+ public void onExternalVibration_thenScreenTurnsOff_doNotCancelVibration() throws Throwable {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF));
+
+ verify(externalVibrationControllerMock, never()).mute();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+ public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ IExternalVibrationController externalVibrationControllerMock =
+ mock(IExternalVibrationController.class);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+
+ assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+ service.mIntentReceiver.onReceive(mContextSpy, new Intent(
+ BackgroundUserSoundNotifier.ACTION_MUTE_SOUND));
+
+ verify(externalVibrationControllerMock, never()).mute();
+ }
+
+ @Test
public void frameworkStats_externalVibration_reportsAllMetrics() throws Exception {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -2501,7 +2633,7 @@
assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
statsInfo.vibrationType);
assertEquals(VibrationAttributes.USAGE_ALARM, statsInfo.usage);
- assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), statsInfo.status);
+ assertEquals(Status.FINISHED.getProtoEnumValue(), statsInfo.status);
assertTrue(statsInfo.totalDurationMillis > 0);
assertTrue(
"Expected vibrator ON for at least 10ms, got " + statsInfo.vibratorOnMillis + "ms",
@@ -2533,7 +2665,7 @@
assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
metrics.vibrationType);
assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage);
- assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+ assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status);
assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
metrics.totalDurationMillis >= 20);
assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
@@ -2586,7 +2718,7 @@
assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED,
metrics.vibrationType);
assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage);
- assertEquals(Vibration.Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status);
+ assertEquals(Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status);
assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
metrics.totalDurationMillis >= 100);
assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
@@ -2647,7 +2779,7 @@
assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
metrics.vibrationType);
assertEquals(VibrationAttributes.USAGE_ALARM, metrics.usage);
- assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+ assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status);
// At least 4 effect/primitive played, 20ms each, plus configured fallback.
assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
@@ -2703,7 +2835,7 @@
VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0);
assertEquals(UID, touchMetrics.uid);
assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage);
- assertEquals(Vibration.Status.CANCELLED_SUPERSEDED.getProtoEnumValue(),
+ assertEquals(Status.CANCELLED_SUPERSEDED.getProtoEnumValue(),
touchMetrics.status);
assertTrue(touchMetrics.endedBySameUid);
assertEquals(VibrationAttributes.USAGE_ALARM, touchMetrics.endedByUsage);
@@ -2712,7 +2844,7 @@
VibrationStats.StatsInfo alarmMetrics = argumentCaptor.getAllValues().get(1);
assertEquals(UID, alarmMetrics.uid);
assertEquals(VibrationAttributes.USAGE_ALARM, alarmMetrics.usage);
- assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), alarmMetrics.status);
+ assertEquals(Status.FINISHED.getProtoEnumValue(), alarmMetrics.status);
assertFalse(alarmMetrics.endedBySameUid);
assertEquals(-1, alarmMetrics.endedByUsage);
assertEquals(VibrationAttributes.USAGE_TOUCH, alarmMetrics.interruptedUsage);
@@ -2742,12 +2874,12 @@
VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0);
assertEquals(UID, touchMetrics.uid);
assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage);
- assertEquals(Vibration.Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status);
+ assertEquals(Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status);
VibrationStats.StatsInfo ringtoneMetrics = argumentCaptor.getAllValues().get(1);
assertEquals(UID, ringtoneMetrics.uid);
assertEquals(VibrationAttributes.USAGE_RINGTONE, ringtoneMetrics.usage);
- assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(),
+ assertEquals(Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(),
ringtoneMetrics.status);
for (VibrationStats.StatsInfo metrics : argumentCaptor.getAllValues()) {
@@ -2814,7 +2946,7 @@
assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
metrics.vibrationType);
assertEquals(VibrationAttributes.USAGE_NOTIFICATION, metrics.usage);
- assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+ assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status);
assertTrue(metrics.totalDurationMillis >= 20);
// vibratorOnMillis accumulates both vibrators, it's 20 for each constant.
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index 808a575..1e1abf2 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -39,7 +39,10 @@
/**
* CallControl provides client side control of a call. Each Call will get an individual CallControl
- * instance in which the client can alter the state of the associated call.
+ * instance in which the client can alter the state of the associated call. Outgoing and incoming
+ * calls should move to active (via {@link CallControl#setActive(Executor, OutcomeReceiver)} or
+ * answered (via {@link CallControl#answer(int, Executor, OutcomeReceiver)} before 60 seconds. If
+ * the new call is not moved to active or answered before 60 seconds, the call will be disconnected.
*
* <p>
* Each method is Transactional meaning that it can succeed or fail. If a transaction succeeds,
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 1df6cf7..ad7d987 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2621,7 +2621,9 @@
}
/**
- * Sets state to ringing (e.g., an inbound ringing connection).
+ * Sets state to ringing (e.g., an inbound ringing connection). The Connection should not be
+ * in STATE_RINGING for more than 60 seconds. After 60 seconds, the Connection will
+ * be disconnected.
*/
public final void setRinging() {
checkImmutable();
@@ -2645,7 +2647,9 @@
}
/**
- * Sets state to dialing (e.g., dialing an outbound connection).
+ * Sets state to dialing (e.g., dialing an outbound connection). The Connection should not be
+ * in STATE_DIALING for more than 60 seconds. After 60 seconds, the Connection will
+ * be disconnected.
*/
public final void setDialing() {
checkImmutable();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cba2eea..13bd5eb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -10014,6 +10014,19 @@
public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING =
"satellite_nidd_apn_name_string";
+ /**
+ * Default value {@code true}, meaning when an emergency call request comes in, if the device is
+ * in emergency satellite mode but hasn't sent the first satellite datagram, then exits
+ * satellite mode to allow the emergency call to go through.
+ *
+ * If {@code false}, the emergency call is always blocked if device is in emergency satellite
+ * mode. Note if device is NOT in emergency satellite mode, emergency call is always allowed.
+ *
+ * @hide
+ */
+ public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL =
+ "satellite_roaming_turn_off_session_for_emergency_call_bool";
+
/** @hide */
@IntDef({
CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
@@ -11276,6 +11289,7 @@
sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, false);
sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, true);
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png
index b2354f8..297244f 100644
--- a/tests/Input/assets/testPointerFillStyle.png
+++ b/tests/Input/assets/testPointerFillStyle.png
Binary files differ