diff options
357 files changed, 11460 insertions, 5093 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index edb119e0afb1..6f8a189c42b8 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -438,10 +438,23 @@ aconfig_declarations { name: "android.companion.virtualdevice.flags-aconfig", package: "android.companion.virtualdevice.flags", container: "system", + exportable: true, srcs: ["core/java/android/companion/virtual/flags/*.aconfig"], } java_aconfig_library { + name: "android.companion.virtualdevice.flags-aconfig-java-export", + aconfig_declarations: "android.companion.virtualdevice.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], + mode: "exported", + min_sdk_version: "30", + apex_available: [ + "//apex_available:platform", + "com.android.permission", + ], +} + +java_aconfig_library { name: "android.companion.virtual.flags-aconfig-java", aconfig_declarations: "android.companion.virtual.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], 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 92dd9be7b737..f4759b8bd35c 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(); + private final boolean mLogToProto; + private final boolean mLogToLogcat; - @Parameters(name="logToProto_{0}_logToLogcat_{1}") + /** + * Generates the parameters used for this test class + */ + @Parameters(name = "LOG_TO_{0}") public static Collection<Object[]> params() { - return Arrays.asList(new Object[][] { - { true, true }, - { true, false }, - { false, true }, - { false, false } - }); + var params = new ArrayList<Object[]>(); + + for (LogTo logTo : LogTo.values()) { + params.add(new Object[] { logTo }); + } + + return params; } - private final boolean mLogToProto; - private final boolean mLogToLogcat; + public ProtoLogPerfTest(LogTo logTo) { + mLogToProto = switch (logTo) { + case ALL, PROTO_ONLY -> true; + case LOGCAT_ONLY, NONE -> false; + }; - public ProtoLogPerfTest(boolean logToProto, boolean logToLogcat) { - mLogToProto = logToProto; - mLogToLogcat = logToLogcat; + mLogToLogcat = switch (logTo) { + case ALL, LOGCAT_ONLY -> true; + case PROTO_ONLY, NONE -> false; + }; } @BeforeClass @@ -66,11 +77,11 @@ public class ProtoLogPerfTest { } @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 @@ public class ProtoLogPerfTest { } @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 @@ public class ProtoLogPerfTest { } @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 @@ public class ProtoLogPerfTest { 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 613b7842ae3a..e5389b4f96fb 100644 --- a/apex/jobscheduler/service/aconfig/job.aconfig +++ b/apex/jobscheduler/service/aconfig/job.aconfig @@ -65,3 +65,13 @@ flag { 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 d65a66c83fcc..be8e304a8101 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 @@ public final class JobServiceContext implements ServiceConnection { 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 @@ public final class JobServiceContext implements ServiceConnection { 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 @@ public final class JobServiceContext implements ServiceConnection { 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 @@ public final class JobServiceContext implements ServiceConnection { 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 2565d6ff3624..4e6dacff290e 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -141,7 +141,7 @@ package android { 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 @@ package android { 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 @@ package android.app.admin { 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); @@ -8756,6 +8756,8 @@ package android.app.appfunctions { method public int getResultCode(); method @NonNull public android.app.appsearch.GenericDocument getResultDocument(); method public boolean isSuccess(); + method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle); + method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR; field public static final String PROPERTY_RETURN_VALUE = "returnValue"; @@ -8767,13 +8769,6 @@ package android.app.appfunctions { field public static final int RESULT_TIMED_OUT = 5; // 0x5 } - @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final class ExecuteAppFunctionResponse.Builder { - ctor public ExecuteAppFunctionResponse.Builder(@NonNull android.app.appsearch.GenericDocument); - ctor public ExecuteAppFunctionResponse.Builder(int, @NonNull String); - method @NonNull public android.app.appfunctions.ExecuteAppFunctionResponse build(); - method @NonNull public android.app.appfunctions.ExecuteAppFunctionResponse.Builder setExtras(@NonNull android.os.Bundle); - } - } package android.app.assist { @@ -34116,7 +34111,7 @@ package android.os { 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 +34161,7 @@ package android.os { 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/api/system-current.txt b/core/api/system-current.txt index f26522b9b919..3637ca763e0a 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -6746,6 +6746,14 @@ package android.hardware.soundtrigger { field public static final int STATUS_OK = 0; // 0x0 } + @FlaggedApi("android.media.soundtrigger.generic_model_api") public static final class SoundTrigger.GenericSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable { + ctor public SoundTrigger.GenericSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], int); + ctor public SoundTrigger.GenericSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[]); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.GenericSoundModel> CREATOR; + } + public static final class SoundTrigger.Keyphrase implements android.os.Parcelable { ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]); method public int describeContents(); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0fc77f00e648..d31d8f27844a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -56,7 +56,6 @@ import static android.Manifest.permission.SET_TIME_ZONE; 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 @@ public class DevicePolicyManager { * @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 f2861fef6b33..29a5048daae6 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -139,6 +139,7 @@ flag { bug: "293441361" } +# Fully rolled out and must not be used. flag { name: "assist_content_user_restriction_enabled" is_exported: true @@ -165,6 +166,7 @@ flag { 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/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java index b6240a79dda8..0ee902632f5f 100644 --- a/core/java/android/app/appfunctions/AppFunctionManager.java +++ b/core/java/android/app/appfunctions/AppFunctionManager.java @@ -50,8 +50,7 @@ public final class AppFunctionManager { * Creates an instance. * * @param mService An interface to the backing service. - * @param context A {@link Context}. - * + * @param context A {@link Context}. * @hide */ public AppFunctionManager(IAppFunctionManager service, Context context) { @@ -108,8 +107,8 @@ public final class AppFunctionManager { } catch (RuntimeException e) { // Ideally shouldn't happen since errors are wrapped into the // response, but we catch it here for additional safety. - callback.accept(new ExecuteAppFunctionResponse.Builder( - getResultCode(e), e.getMessage()).build()); + callback.accept(ExecuteAppFunctionResponse.newFailure( + getResultCode(e), e.getMessage(), /*extras=*/ null)); } } }); diff --git a/core/java/android/app/appfunctions/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java new file mode 100644 index 000000000000..3169f0e48877 --- /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 000000000000..fdd12b0b49d3 --- /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/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java index 6259d165d132..22bc9088f5e4 100644 --- a/core/java/android/app/appfunctions/AppFunctionService.java +++ b/core/java/android/app/appfunctions/AppFunctionService.java @@ -81,8 +81,9 @@ public abstract class AppFunctionService extends Service { // Apps should handle exceptions. But if they don't, report the error on // behalf of them. safeCallback.onResult( - new ExecuteAppFunctionResponse.Builder( - getResultCode(ex), getExceptionMessage(ex)).build()); + ExecuteAppFunctionResponse.newFailure( + getResultCode(ex), + ex.getMessage(), /*extras=*/ null)); } } }; @@ -117,8 +118,4 @@ public abstract class AppFunctionService extends Service { public abstract void onExecuteFunction( @NonNull ExecuteAppFunctionRequest request, @NonNull Consumer<ExecuteAppFunctionResponse> callback); - - private String getExceptionMessage(Exception exception) { - return exception.getMessage() == null ? "" : exception.getMessage(); - } } diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java new file mode 100644 index 000000000000..6d4172aff8b3 --- /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/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java index 9fb3375a2446..58d4088f733e 100644 --- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java +++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java @@ -162,6 +162,55 @@ public final class ExecuteAppFunctionResponse implements Parcelable { } /** + * Returns a successful response. + * + * @param resultDocument The return value of the executed function. + * @param extras The additional metadata data relevant to this function execution + * response. + */ + @NonNull + @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) + public static ExecuteAppFunctionResponse newSuccess(@NonNull GenericDocument resultDocument, + @Nullable Bundle extras) { + Objects.requireNonNull(resultDocument); + Bundle actualExtras = getActualExtras(extras); + GenericDocumentWrapper resultDocumentWrapper = new GenericDocumentWrapper(resultDocument); + + return new ExecuteAppFunctionResponse( + resultDocumentWrapper, actualExtras, RESULT_OK, /*errorMessage=*/null); + } + + /** + * Returns a failure response. + * + * @param resultCode The result code of the app function execution. + * @param extras The additional metadata data relevant to this function execution + * response. + * @param errorMessage The error message associated with the result, if any. + */ + @NonNull + @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) + public static ExecuteAppFunctionResponse newFailure(@ResultCode int resultCode, + @Nullable String errorMessage, + @Nullable Bundle extras) { + if (resultCode == RESULT_OK) { + throw new IllegalArgumentException("resultCode must not be RESULT_OK"); + } + Bundle actualExtras = getActualExtras(extras); + GenericDocumentWrapper emptyWrapper = new GenericDocumentWrapper( + new GenericDocument.Builder<>("", "", "").build()); + return new ExecuteAppFunctionResponse( + emptyWrapper, actualExtras, resultCode, errorMessage); + } + + private static Bundle getActualExtras(@Nullable Bundle extras) { + if (extras == null) { + return Bundle.EMPTY; + } + return extras; + } + + /** * Returns a generic document containing the return value of the executed function. * * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.</p> @@ -250,64 +299,4 @@ public final class ExecuteAppFunctionResponse implements Parcelable { @Retention(RetentionPolicy.SOURCE) public @interface ResultCode { } - - /** - * The builder for creating {@link ExecuteAppFunctionResponse} instances. - */ - @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) - public static final class Builder { - @NonNull - private GenericDocument mResultDocument = - new GenericDocument.Builder<>("", "", "").build(); - @NonNull - private Bundle mExtras = Bundle.EMPTY; - private int mResultCode; - @Nullable - private String mErrorMessage; - - /** - * Creates a new builder for {@link ExecuteAppFunctionResponse}. - */ - private Builder() { - } - - /** - * Creates a new builder for {@link ExecuteAppFunctionResponse} to build a success response - * with a result code of {@link #RESULT_OK} and a resultDocument. - */ - public Builder(@NonNull GenericDocument resultDocument) { - Objects.requireNonNull(resultDocument); - mResultDocument = resultDocument; - mResultCode = RESULT_OK; - } - - /** - * Creates a new builder for {@link ExecuteAppFunctionResponse} to build an error response - * with a result code and an error message. - */ - public Builder(@ResultCode int resultCode, - @NonNull String errorMessage) { - mResultCode = resultCode; - mErrorMessage = Objects.requireNonNull(errorMessage); - } - - /** - * Sets the extras of the app function execution. - */ - @NonNull - public Builder setExtras(@NonNull Bundle extras) { - mExtras = Objects.requireNonNull(extras); - return this; - } - - /** - * Builds the {@link ExecuteAppFunctionResponse} instance. - */ - @NonNull - public ExecuteAppFunctionResponse build() { - return new ExecuteAppFunctionResponse( - new GenericDocumentWrapper(mResultDocument), - mExtras, mResultCode, mErrorMessage); - } - } } diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig index ee9114f74092..93d62cfeb537 100644 --- a/core/java/android/companion/flags.aconfig +++ b/core/java/android/companion/flags.aconfig @@ -10,13 +10,6 @@ flag { } 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/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index b4c36e1bc513..22a9ccf425c2 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -50,6 +50,7 @@ flag { name: "activity_control_api" description: "Enable APIs for fine grained activity policy, fallback and callbacks" bug: "333443509" + is_exported: true } flag { diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index d7a517a2c067..ffed5366d31b 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -629,11 +629,10 @@ public final class AttributionSource implements Parcelable { * 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 final class AttributionSource implements Parcelable { */ 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 @@ public final class AttributionSource implements Parcelable { 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 final class AttributionSource implements Parcelable { */ public @NonNull Builder setPid(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x2; mAttributionSourceState.pid = value; return this; } @@ -677,7 +685,6 @@ public final class AttributionSource implements Parcelable { */ public @NonNull Builder setPackageName(@Nullable String value) { checkNotUsed(); - mBuilderFieldsSet |= 0x4; mAttributionSourceState.packageName = value; return this; } @@ -687,7 +694,6 @@ public final class AttributionSource implements Parcelable { */ public @NonNull Builder setAttributionTag(@Nullable String value) { checkNotUsed(); - mBuilderFieldsSet |= 0x8; mAttributionSourceState.attributionTag = value; return this; } @@ -718,11 +724,11 @@ public final class AttributionSource implements Parcelable { */ @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 @@ public final class AttributionSource implements Parcelable { @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 final class AttributionSource implements Parcelable { */ 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 @@ public final class AttributionSource implements Parcelable { if (value == null) { throw new IllegalArgumentException("Null AttributionSource not permitted."); } - mBuilderFieldsSet |= 0x40; mAttributionSourceState.next = new AttributionSourceState[]{value.mAttributionSourceState}; return this; @@ -769,28 +772,7 @@ public final class AttributionSource implements Parcelable { /** 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 @@ public final class AttributionSource implements Parcelable { } 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/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index 03cf7c5f926c..2a362381a003 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -567,7 +567,7 @@ public final class InputManagerGlobal { Objects.requireNonNull(listener, "listener must not be null"); synchronized (mOnTabletModeChangedListeners) { - if (mOnTabletModeChangedListeners == null) { + if (mOnTabletModeChangedListeners.isEmpty()) { initializeTabletModeListenerLocked(); } int idx = findOnTabletModeChangedListenerLocked(listener); diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index bfff4dbdd627..9f3e3ad8c01e 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -29,6 +29,7 @@ import static android.system.OsConstants.EPIPE; import static java.util.Objects.requireNonNull; import android.annotation.ElapsedRealtimeLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -874,10 +875,9 @@ public class SoundTrigger { /***************************************************************************** * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound * patterns. - * - * @hide ****************************************************************************/ - public static class GenericSoundModel extends SoundModel implements Parcelable { + @FlaggedApi(android.media.soundtrigger.Flags.FLAG_GENERIC_MODEL_API) + public static final class GenericSoundModel extends SoundModel implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR = new Parcelable.Creator<GenericSoundModel>() { @@ -890,11 +890,26 @@ public class SoundTrigger { } }; + /** + * Constructor for {@link GenericSoundModel} with version. + * + * @param uuid Unique identifier for this sound model. + * @param vendorUuid Unique vendor identifier for this sound model. + * @param data Opaque data for this sound model. + * @param version Vendor-specific version number of this sound model. + */ public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, int version) { super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version); } + /** + * Constructor for {@link GenericSoundModel} without version. The version is set to -1. + * + * @param uuid Unique identifier for this sound model. + * @param vendorUuid Unique vendor identifier for this sound model. + * @param data Opaque data for this sound model. + */ @UnsupportedAppUsage public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data) { @@ -919,7 +934,7 @@ public class SoundTrigger { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(getUuid().toString()); if (getVendorUuid() == null) { dest.writeInt(-1); diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 92b630f3063a..80f39bfbdc21 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -46,13 +46,13 @@ import java.lang.reflect.Modifier; * 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 @@ public class Handler { */ 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 @@ public class Handler { * 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 @@ public class Handler { /** * 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 @@ public class Handler { { 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 @@ public class Handler { } /** - * + * * 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 @@ public class Handler { { 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 @@ public class Handler { /** * 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 @@ public class Handler { * @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 class Handler { 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 @@ public class Handler { * {@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 @@ public class Handler { * 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 class Handler { 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 @@ public class Handler { * <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 @@ public class Handler { * 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 @@ public class Handler { /** * 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 @@ public class Handler { /** * 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 @@ public class Handler { } /** - * 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 @@ public class Handler { * 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 @@ public class Handler { * 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 @@ public class Handler { * <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 @@ public class Handler { /** * 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 @@ public class Handler { } // 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 a1db9be0b693..702fdc2bbaa6 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -41,6 +41,9 @@ public final class Message implements Parcelable { * 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 c801fabf9427..46ae9d8682ee 100644 --- a/core/java/android/os/SharedMemory.java +++ b/core/java/android/os/SharedMemory.java @@ -379,6 +379,14 @@ public final class SharedMemory implements Parcelable, Closeable { private int mReferenceCount; private MemoryRegistration(int size) { + // Round up to the nearest page size + final int PAGE_SIZE = OsConstants._SC_PAGE_SIZE; + if (PAGE_SIZE > 0) { + 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 536eca6f9a0f..f1ec0e4e9bdc 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1978,7 +1978,6 @@ public class UserManager { * @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 @@ public class UserManager { * @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 fe536a6e4e68..22583acb75ce 100644 --- a/core/java/android/util/Half.java +++ b/core/java/android/util/Half.java @@ -19,6 +19,7 @@ package android.util; 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 @@ import libcore.util.FP16; * <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/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index a4cea3364998..ab29df357268 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1051,6 +1051,52 @@ public final class AccessibilityManager { } /** + * Registers callback for when user initialization has completed. + * Does nothing if the same callback is already registered. + * + * @param callback The callback to be registered + * @hide + */ + public void registerUserInitializationCompleteCallback( + @NonNull IUserInitializationCompleteCallback callback) { + IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.registerUserInitializationCompleteCallback(callback); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error while registering userInitializationCompleteCallback. ", re); + } + } + + /** + * Unregisters callback for when user initialization has completed. + * + * @param callback The callback to be unregistered + * @hide + */ + public void unregisterUserInitializationCompleteCallback( + @NonNull IUserInitializationCompleteCallback callback) { + IAccessibilityManager service; + synchronized (mLock) { + service = getServiceLocked(); + if (service == null) { + return; + } + } + try { + service.unregisterUserInitializationCompleteCallback(callback); + } catch (RemoteException re) { + Log.e(LOG_TAG, + "Error while unregistering userInitializationCompleteCallback. ", re); + } + } + + /** * Whether the current accessibility request comes from an * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool} * property set to true. diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 72a1fe424906..bf79a2c6c6ea 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -29,6 +29,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import android.view.accessibility.IAccessibilityManagerClient; import android.view.accessibility.AccessibilityWindowAttributes; import android.view.accessibility.IMagnificationConnection; +import android.view.accessibility.IUserInitializationCompleteCallback; import android.view.InputEvent; import android.view.IWindow; import android.view.MagnificationSpec; @@ -192,4 +193,10 @@ interface IAccessibilityManager { @EnforcePermission("MANAGE_ACCESSIBILITY") Bundle getA11yFeatureToTileMap(int userId); + + @RequiresNoPermission + void registerUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback); + + @RequiresNoPermission + void unregisterUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback); } diff --git a/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl new file mode 100644 index 000000000000..fe6c8e25dd00 --- /dev/null +++ b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl @@ -0,0 +1,35 @@ +/* + * 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.view.accessibility; + +/** + * A callback for when a new user finishes initializing + * NOTE: Must remain a oneway interface, as it is called from system_server while holding a lock. + * oneway allows it to return immediately and not hold the lock for longer than is necessary. + * @hide + */ + +oneway interface IUserInitializationCompleteCallback { + + /** + * Called when a user initialization completes. + * + * @param userId the id of the initialized user + */ + @RequiresNoPermission + void onUserInitializationComplete(int userId); +} diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl index 58b5757a47bb..b8a11cf063e2 100644 --- a/core/java/android/window/ITaskFragmentOrganizerController.aidl +++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl @@ -47,6 +47,19 @@ interface ITaskFragmentOrganizerController { void unregisterOrganizer(in ITaskFragmentOrganizer organizer); /** + * Registers remote animations per transition type for the organizer. It will override the + * animations if the transition only contains windows that belong to the organized + * TaskFragments in the given Task. + */ + void registerRemoteAnimations(in ITaskFragmentOrganizer organizer, + in RemoteAnimationDefinition definition); + + /** + * Unregisters remote animations per transition type for the organizer. + */ + void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer); + + /** * Saves the state in the system, where the state can be restored if the process of * the TaskFragmentOrganizer is restarted. */ diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java index 027d323bfcd1..4cc0d8a77a2b 100644 --- a/core/java/android/window/TaskFragmentOrganizer.java +++ b/core/java/android/window/TaskFragmentOrganizer.java @@ -34,6 +34,7 @@ import android.app.Activity; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.view.RemoteAnimationDefinition; import android.view.WindowManager; import com.android.window.flags.Flags; @@ -225,6 +226,34 @@ public class TaskFragmentOrganizer extends WindowOrganizer { } /** + * Registers remote animations per transition type for the organizer. It will override the + * animations if the transition only contains windows that belong to the organized + * TaskFragments, and at least one of the transition window is embedded (not filling the Task). + * @hide + */ + @CallSuper + public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) { + try { + getController().registerRemoteAnimations(mInterface, definition); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters remote animations per transition type for the organizer. + * @hide + */ + @CallSuper + public void unregisterRemoteAnimations() { + try { + getController().unregisterRemoteAnimations(mInterface); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Saves the state in the system, where the state can be restored if the process of * the TaskFragmentOrganizer is restarted. * diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig index 8c6721a7e96a..efacc346ac0a 100644 --- a/core/java/android/window/flags/wallpaper_manager.aconfig +++ b/core/java/android/window/flags/wallpaper_manager.aconfig @@ -49,3 +49,13 @@ flag { 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 e14249cc028b..e0529b339d4a 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -3333,6 +3333,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java index f8a143693c83..1938cdb0ba84 100644 --- a/core/java/com/android/internal/statusbar/StatusBarIcon.java +++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java @@ -51,6 +51,19 @@ public class StatusBarIcon implements Parcelable { ResourceIcon } + public enum Shape { + /** + * Icon view should use WRAP_CONTENT -- so that the horizontal space occupied depends on the + * icon's shape (skinny/fat icons take less/more). Most icons will want to use this option + * for a nicer-looking overall spacing in the status bar, as long as the icon is "known" + * (i.e. not coming from a 3P package). + */ + WRAP_CONTENT, + + /** Icon should always be displayed in a space as wide as the status bar is tall. */ + FIXED_SPACE, + } + public UserHandle user; public String pkg; public Icon icon; @@ -59,6 +72,7 @@ public class StatusBarIcon implements Parcelable { public int number; public CharSequence contentDescription; public Type type; + public Shape shape; /** * Optional {@link Drawable} corresponding to {@link #icon}. This field is not parcelable, so @@ -68,7 +82,7 @@ public class StatusBarIcon implements Parcelable { @Nullable public Drawable preloadedIcon; public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number, - CharSequence contentDescription, Type type) { + CharSequence contentDescription, Type type, Shape shape) { if (icon.getType() == Icon.TYPE_RESOURCE && TextUtils.isEmpty(icon.getResPackage())) { // This is an odd situation where someone's managed to hand us an icon without a @@ -83,6 +97,13 @@ public class StatusBarIcon implements Parcelable { this.number = number; this.contentDescription = contentDescription; this.type = type; + this.shape = shape; + } + + public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number, + CharSequence contentDescription, Type type) { + this(user, resPackage, icon, iconLevel, number, contentDescription, type, + Shape.WRAP_CONTENT); } public StatusBarIcon(String iconPackage, UserHandle user, @@ -107,7 +128,7 @@ public class StatusBarIcon implements Parcelable { @Override public StatusBarIcon clone() { StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon, - this.iconLevel, this.number, this.contentDescription, this.type); + this.iconLevel, this.number, this.contentDescription, this.type, this.shape); that.visible = this.visible; that.preloadedIcon = this.preloadedIcon; return that; @@ -129,6 +150,7 @@ public class StatusBarIcon implements Parcelable { this.number = in.readInt(); this.contentDescription = in.readCharSequence(); this.type = Type.valueOf(in.readString()); + this.shape = Shape.valueOf(in.readString()); } public void writeToParcel(Parcel out, int flags) { @@ -140,6 +162,7 @@ public class StatusBarIcon implements Parcelable { out.writeInt(this.number); out.writeCharSequence(this.contentDescription); out.writeString(this.type.name()); + out.writeString(this.shape.name()); } public int describeContents() { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a89ab451e978..17ff2ebc4f20 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 94bde68fdf11..127dbfd25b09 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 000000000000..c23c1484cf59 --- /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 000000000000..b01648838695 --- /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/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java index b183ecb50591..149e132a0df4 100644 --- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java +++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Icon; import android.os.Parcel; import android.os.UserHandle; @@ -69,22 +70,22 @@ public class StatusBarIconTest { assertThat(copy.preloadedIcon).isEqualTo(original.preloadedIcon); } - private static StatusBarIcon newStatusBarIcon() { final UserHandle dummyUserHandle = UserHandle.of(100); final String dummyIconPackageName = "com.android.internal.statusbar.test"; - final int dummyIconId = 123; + final Icon dummyIcon = Icon.createWithResource(dummyIconPackageName, 123); final int dummyIconLevel = 1; final int dummyIconNumber = 2; final CharSequence dummyIconContentDescription = "dummyIcon"; return new StatusBarIcon( - dummyIconPackageName, dummyUserHandle, - dummyIconId, + dummyIconPackageName, + dummyIcon, dummyIconLevel, dummyIconNumber, dummyIconContentDescription, - StatusBarIcon.Type.SystemIcon); + StatusBarIcon.Type.SystemIcon, + StatusBarIcon.Shape.FIXED_SPACE); } private static void assertSerializableFieldsEqual(StatusBarIcon copy, StatusBarIcon original) { @@ -96,6 +97,7 @@ public class StatusBarIconTest { assertThat(copy.number).isEqualTo(original.number); assertThat(copy.contentDescription).isEqualTo(original.contentDescription); assertThat(copy.type).isEqualTo(original.type); + assertThat(copy.shape).isEqualTo(original.shape); } private static StatusBarIcon parcelAndUnparcel(StatusBarIcon original) { diff --git a/data/keyboards/Vendor_0957_Product_0033.idc b/data/keyboards/Vendor_0957_Product_0033.idc new file mode 100644 index 000000000000..7dfbe2cced34 --- /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 a99e20101c3b..683f6149a203 100644 --- a/graphics/java/android/graphics/Matrix44.java +++ b/graphics/java/android/graphics/Matrix44.java @@ -19,6 +19,7 @@ package android.graphics; 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 @@ import java.util.Arrays; * 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 618e6dcc4433..c7b89412cc47 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; 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 @@ import java.lang.annotation.RetentionPolicy; * @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 748d66cb5f6c..76c1715475ae 100644 --- a/graphics/java/android/graphics/ParcelableColorSpace.java +++ b/graphics/java/android/graphics/ParcelableColorSpace.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; 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 @@ import android.os.Parcelable; * {@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 3ec5b9cc7dae..a872e03db3b5 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/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java index 69a68c85a2fe..0726624a05f8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java @@ -24,6 +24,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import java.util.ArrayList; import java.util.List; /** @@ -80,9 +81,14 @@ class BackupHelper { } if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers); + final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = new ArrayList<>( + taskContainers.size()); + for (TaskContainer taskContainer : taskContainers) { + parcelableTaskContainerDataList.add(taskContainer.getParcelableData()); + } final Bundle state = new Bundle(); - state.setClassLoader(TaskContainer.class.getClassLoader()); - state.putParcelableList(KEY_TASK_CONTAINERS, taskContainers); + state.setClassLoader(ParcelableTaskContainerData.class.getClassLoader()); + state.putParcelableList(KEY_TASK_CONTAINERS, parcelableTaskContainerDataList); mController.setSavedState(state); } @@ -91,10 +97,12 @@ class BackupHelper { return; } - final List<TaskContainer> taskContainers = savedState.getParcelableArrayList( - KEY_TASK_CONTAINERS, TaskContainer.class); - for (TaskContainer taskContainer : taskContainers) { - if (DEBUG) Log.d(TAG, "restore task " + taskContainer.getTaskId()); + final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = + savedState.getParcelableArrayList(KEY_TASK_CONTAINERS, + ParcelableTaskContainerData.class); + for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) { + final TaskContainer taskContainer = new TaskContainer(data, mController); + if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId()); // TODO(b/289875940): implement the TaskContainer restoration. } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index 1eb95c1efb08..9ea2943bc6da 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -70,6 +70,10 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { @NonNull private final TaskFragmentCallback mCallback; + @VisibleForTesting + @Nullable + TaskFragmentAnimationController mAnimationController; + /** * Callback that notifies the controller about changes to task fragments. */ @@ -87,6 +91,25 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { mCallback = callback; } + @Override + public void unregisterOrganizer() { + if (mAnimationController != null) { + mAnimationController.unregisterRemoteAnimations(); + mAnimationController = null; + } + super.unregisterOrganizer(); + } + + /** + * Overrides the animation for transitions of embedded activities organized by this organizer. + */ + void overrideSplitAnimation() { + if (mAnimationController == null) { + mAnimationController = new TaskFragmentAnimationController(this); + } + mAnimationController.registerRemoteAnimations(); + } + /** * Starts a new Activity and puts it into split with an existing Activity side-by-side. * @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java new file mode 100644 index 000000000000..817cfce69b2e --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java @@ -0,0 +1,116 @@ +/* + * 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 androidx.window.extensions.embedding; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This class holds the Parcelable data of a {@link SplitContainer}. + */ +class ParcelableSplitContainerData implements Parcelable { + + /** + * A reference to the target {@link SplitContainer} that owns the data. This will not be + * parcelled and will be {@code null} when the data is created from a parcel. + */ + @Nullable + final SplitContainer mSplitContainer; + + @NonNull + final IBinder mToken; + + @NonNull + private final IBinder mPrimaryContainerToken; + + @NonNull + private final IBinder mSecondaryContainerToken; + + // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule. + @Nullable + final String mSplitRuleTag; + + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + final boolean mIsPrimaryContainerMutable; + + ParcelableSplitContainerData(@NonNull SplitContainer splitContainer, @NonNull IBinder token, + @NonNull IBinder primaryContainerToken, @NonNull IBinder secondaryContainerToken, + @Nullable String splitRuleTag, boolean isPrimaryContainerMutable) { + mSplitContainer = splitContainer; + mToken = token; + mPrimaryContainerToken = primaryContainerToken; + mSecondaryContainerToken = secondaryContainerToken; + mSplitRuleTag = splitRuleTag; + mIsPrimaryContainerMutable = isPrimaryContainerMutable; + } + + private ParcelableSplitContainerData(Parcel in) { + mSplitContainer = null; + mToken = in.readStrongBinder(); + mPrimaryContainerToken = in.readStrongBinder(); + mSecondaryContainerToken = in.readStrongBinder(); + mSplitRuleTag = in.readString(); + mIsPrimaryContainerMutable = in.readBoolean(); + } + + public static final Creator<ParcelableSplitContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableSplitContainerData createFromParcel(Parcel in) { + return new ParcelableSplitContainerData(in); + } + + @Override + public ParcelableSplitContainerData[] newArray(int size) { + return new ParcelableSplitContainerData[size]; + } + }; + + @NonNull + private IBinder getPrimaryContainerToken() { + return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken() + : mPrimaryContainerToken; + } + + @NonNull + private IBinder getSecondaryContainerToken() { + return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken() + : mSecondaryContainerToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeStrongBinder(getPrimaryContainerToken()); + dest.writeStrongBinder(getSecondaryContainerToken()); + dest.writeString(mSplitRuleTag); + dest.writeBoolean(mIsPrimaryContainerMutable); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java new file mode 100644 index 000000000000..7377d005cda4 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java @@ -0,0 +1,127 @@ +/* + * 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 androidx.window.extensions.embedding; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class holds the Parcelable data of a {@link TaskContainer}. + */ +class ParcelableTaskContainerData implements Parcelable { + + /** + * A reference to the target {@link TaskContainer} that owns the data. This will not be + * parcelled and will be {@code null} when the data is created from a parcel. + */ + @Nullable + final TaskContainer mTaskContainer; + + /** + * The unique task id. + */ + final int mTaskId; + + /** + * The parcelable data of the active TaskFragmentContainers in this Task. + * Note that this will only be populated before parcelling, and will not be copied when + * making a new instance copy. + */ + @NonNull + private final List<ParcelableTaskFragmentContainerData> + mParcelableTaskFragmentContainerDataList = new ArrayList<>(); + + /** + * The parcelable data of the SplitContainers in this Task. + * Note that this will only be populated before parcelling, and will not be copied when + * making a new instance copy. + */ + @NonNull + private final List<ParcelableSplitContainerData> mParcelableSplitContainerDataList = + new ArrayList<>(); + + ParcelableTaskContainerData(int taskId, @NonNull TaskContainer taskContainer) { + if (taskId == INVALID_TASK_ID) { + throw new IllegalArgumentException("Invalid Task id"); + } + + mTaskId = taskId; + mTaskContainer = taskContainer; + } + + ParcelableTaskContainerData(@NonNull ParcelableTaskContainerData data, + @NonNull TaskContainer taskContainer) { + mTaskId = data.mTaskId; + mTaskContainer = taskContainer; + } + + private ParcelableTaskContainerData(Parcel in) { + mTaskId = in.readInt(); + mTaskContainer = null; + in.readParcelableList(mParcelableTaskFragmentContainerDataList, + ParcelableTaskFragmentContainerData.class.getClassLoader(), + ParcelableTaskFragmentContainerData.class); + in.readParcelableList(mParcelableSplitContainerDataList, + ParcelableSplitContainerData.class.getClassLoader(), + ParcelableSplitContainerData.class); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mTaskId); + dest.writeParcelableList(getParcelableTaskFragmentContainerDataList(), flags); + dest.writeParcelableList(getParcelableSplitContainerDataList(), flags); + } + + @NonNull + List<? extends ParcelableTaskFragmentContainerData> + getParcelableTaskFragmentContainerDataList() { + return mTaskContainer != null ? mTaskContainer.getParcelableTaskFragmentContainerDataList() + : mParcelableTaskFragmentContainerDataList; + } + + @NonNull + List<? extends ParcelableSplitContainerData> getParcelableSplitContainerDataList() { + return mTaskContainer != null ? mTaskContainer.getParcelableSplitContainerDataList() + : mParcelableSplitContainerDataList; + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator<ParcelableTaskContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableTaskContainerData createFromParcel(Parcel in) { + return new ParcelableTaskContainerData(in); + } + + @Override + public ParcelableTaskContainerData[] newArray(int size) { + return new ParcelableTaskContainerData[size]; + } + }; +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java new file mode 100644 index 000000000000..a79a89a210ac --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java @@ -0,0 +1,105 @@ +/* + * 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 androidx.window.extensions.embedding; + +import android.app.Activity; +import android.graphics.Rect; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * This class holds the Parcelable data of a {@link TaskFragmentContainer}. + */ +class ParcelableTaskFragmentContainerData implements Parcelable { + + /** + * Client-created token that uniquely identifies the task fragment container instance. + */ + @NonNull + final IBinder mToken; + + /** + * The tag specified in launch options. {@code null} if this taskFragment container is not an + * overlay container. + */ + @Nullable + final String mOverlayTag; + + /** + * The associated {@link Activity#getActivityToken()} of the overlay container. + * Must be {@code null} for non-overlay container. + * <p> + * If an overlay container is associated with an activity, this overlay container will be + * dismissed when the associated activity is destroyed. If the overlay container is visible, + * activity will be launched on top of the overlay container and expanded to fill the parent + * container. + */ + @Nullable + final IBinder mAssociatedActivityToken; + + /** + * Bounds that were requested last via {@link android.window.WindowContainerTransaction}. + */ + @NonNull + final Rect mLastRequestedBounds; + + ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag, + @Nullable IBinder associatedActivityToken) { + mToken = token; + mOverlayTag = overlayTag; + mAssociatedActivityToken = associatedActivityToken; + mLastRequestedBounds = new Rect(); + } + + private ParcelableTaskFragmentContainerData(Parcel in) { + mToken = in.readStrongBinder(); + mOverlayTag = in.readString(); + mAssociatedActivityToken = in.readStrongBinder(); + mLastRequestedBounds = in.readTypedObject(Rect.CREATOR); + } + + public static final Creator<ParcelableTaskFragmentContainerData> CREATOR = new Creator<>() { + @Override + public ParcelableTaskFragmentContainerData createFromParcel(Parcel in) { + return new ParcelableTaskFragmentContainerData(in); + } + + @Override + public ParcelableTaskFragmentContainerData[] newArray(int size) { + return new ParcelableTaskFragmentContainerData[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + dest.writeString(mOverlayTag); + dest.writeStrongBinder(mAssociatedActivityToken); + dest.writeTypedObject(mLastRequestedBounds, flags); + } + +} + diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 39cfacec8447..6d436ec01d98 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -33,6 +33,8 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull + private final ParcelableSplitContainerData mParcelableData; + @NonNull private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @@ -44,16 +46,6 @@ class SplitContainer { /** @see SplitContainer#getDefaultSplitAttributes() */ @NonNull private SplitAttributes mDefaultSplitAttributes; - @NonNull - private final IBinder mToken; - - /** - * Whether the selection of which container is primary can be changed at runtime. Runtime - * updates is currently possible only for {@link SplitPinContainer} - * - * @see SplitPinContainer - */ - private final boolean mIsPrimaryContainerMutable; SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @@ -69,13 +61,14 @@ class SplitContainer { @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { + mParcelableData = new ParcelableSplitContainerData(this, new Binder("SplitContainer"), + primaryContainer.getToken(), secondaryContainer.getToken(), splitRule.getTag(), + isPrimaryContainerMutable); mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; - mToken = new Binder("SplitContainer"); - mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -94,7 +87,7 @@ class SplitContainer { } void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { - if (!mIsPrimaryContainerMutable) { + if (!mParcelableData.mIsPrimaryContainerMutable) { throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); } mPrimaryContainer = primaryContainer; @@ -150,7 +143,12 @@ class SplitContainer { @NonNull IBinder getToken() { - return mToken; + return mParcelableData.mToken; + } + + @NonNull + ParcelableSplitContainerData getParcelableData() { + return mParcelableData; } /** @@ -201,7 +199,7 @@ class SplitContainer { return null; } return new SplitInfo(primaryActivityStack, secondaryActivityStack, - mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken)); + mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mParcelableData.mToken)); } static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 24b56ae471e5..5657647ff5c0 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -116,6 +116,7 @@ import java.util.function.BiConsumer; public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback, ActivityEmbeddingComponent, DividerPresenter.DragEventCallback { static final String TAG = "SplitController"; + static final boolean ENABLE_SHELL_TRANSITIONS = true; // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without // association. It's not set in WM Extensions nor Wm Jetpack library currently. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index fb8efc4ad490..abc7b291fc32 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -169,12 +169,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { mController = controller; final Bundle outSavedState = new Bundle(); if (Flags.aeBackStackRestore()) { - outSavedState.setClassLoader(TaskContainer.class.getClassLoader()); + outSavedState.setClassLoader(ParcelableTaskContainerData.class.getClassLoader()); registerOrganizer(false /* isSystemOrganizer */, outSavedState); } else { registerOrganizer(); } mBackupHelper = new BackupHelper(controller, outSavedState); + if (!SplitController.ENABLE_SHELL_TRANSITIONS) { + // TODO(b/207070762): cleanup with legacy app transition + // Animation will be handled by WM Shell when Shell transition is enabled. + overrideSplitAnimation(); + } } void scheduleBackup() { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 5795e8da18c2..82dfda58fc75 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -16,7 +16,6 @@ package androidx.window.extensions.embedding; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -32,8 +31,6 @@ import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; -import android.os.Parcel; -import android.os.Parcelable; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; @@ -57,11 +54,12 @@ import java.util.List; import java.util.Set; /** Represents TaskFragments and split pairs below a Task. */ -class TaskContainer implements Parcelable { +class TaskContainer { private static final String TAG = TaskContainer.class.getSimpleName(); - /** The unique task id. */ - private final int mTaskId; + /** Parcelable data of this TaskContainer. */ + @NonNull + private final ParcelableTaskContainerData mParcelableTaskContainerData; /** Active TaskFragments in this Task. */ @NonNull @@ -130,11 +128,9 @@ class TaskContainer implements Parcelable { * @param splitController The {@link SplitController}. */ TaskContainer(int taskId, @NonNull Activity activityInTask, - @Nullable SplitController splitController) { - if (taskId == INVALID_TASK_ID) { - throw new IllegalArgumentException("Invalid Task id"); - } - mTaskId = taskId; + @NonNull SplitController splitController) { + mParcelableTaskContainerData = new ParcelableTaskContainerData(taskId, this); + final TaskProperties taskProperties = TaskProperties .getTaskPropertiesFromActivity(activityInTask); mInfo = new TaskFragmentParentInfo( @@ -148,8 +144,44 @@ class TaskContainer implements Parcelable { mSplitController = splitController; } + /** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */ + TaskContainer(@NonNull ParcelableTaskContainerData data, + @NonNull SplitController splitController) { + mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this); + mSplitController = splitController; + for (ParcelableTaskFragmentContainerData tfData : + data.getParcelableTaskFragmentContainerDataList()) { + final TaskFragmentContainer container = + new TaskFragmentContainer(tfData, splitController, this); + mContainers.add(container); + } + } + + @NonNull + ParcelableTaskContainerData getParcelableData() { + return mParcelableTaskContainerData; + } + + @NonNull + List<ParcelableTaskFragmentContainerData> getParcelableTaskFragmentContainerDataList() { + final List<ParcelableTaskFragmentContainerData> data = new ArrayList<>(mContainers.size()); + for (TaskFragmentContainer container : mContainers) { + data.add(container.getParcelableData()); + } + return data; + } + + @NonNull + List<ParcelableSplitContainerData> getParcelableSplitContainerDataList() { + final List<ParcelableSplitContainerData> data = new ArrayList<>(mSplitContainers.size()); + for (SplitContainer splitContainer : mSplitContainers) { + data.add(splitContainer.getParcelableData()); + } + return data; + } + int getTaskId() { - return mTaskId; + return mParcelableTaskContainerData.mTaskId; } int getDisplayId() { @@ -680,34 +712,6 @@ class TaskContainer implements Parcelable { return activityStacks; } - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mTaskId); - // TODO(b/289875940) - } - - protected TaskContainer(Parcel in) { - mTaskId = in.readInt(); - // TODO(b/289875940) - } - - public static final Creator<TaskContainer> CREATOR = new Creator<>() { - @Override - public TaskContainer createFromParcel(Parcel in) { - return new TaskContainer(in); - } - - @Override - public TaskContainer[] newArray(int size) { - return new TaskContainer[size]; - } - }; - /** A wrapper class which contains the information of {@link TaskContainer} */ static final class TaskProperties { private final int mDisplayId; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java new file mode 100644 index 000000000000..33220c44a3b5 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import static android.graphics.Matrix.MTRANS_X; +import static android.graphics.Matrix.MTRANS_Y; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; + +import android.graphics.Point; +import android.graphics.Rect; +import android.view.Choreographer; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.animation.Animation; +import android.view.animation.Transformation; + +import androidx.annotation.NonNull; + +/** + * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}. + * + * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close. + */ +class TaskFragmentAnimationAdapter { + + /** + * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer. + */ + private static final int LAYER_NO_OVERRIDE = -1; + + @NonNull + final Animation mAnimation; + @NonNull + final RemoteAnimationTarget mTarget; + @NonNull + final SurfaceControl mLeash; + /** Area in absolute coordinate that the animation surface shouldn't go beyond. */ + @NonNull + private final Rect mWholeAnimationBounds = new Rect(); + /** + * Area in absolute coordinate that should represent all the content to show for this window. + * This should be the end bounds for opening window, and start bounds for closing window in case + * the window is resizing during the open/close transition. + */ + @NonNull + private final Rect mContentBounds = new Rect(); + /** Offset relative to the window parent surface for {@link #mContentBounds}. */ + @NonNull + private final Point mContentRelOffset = new Point(); + + @NonNull + final Transformation mTransformation = new Transformation(); + @NonNull + final float[] mMatrix = new float[9]; + @NonNull + final float[] mVecs = new float[4]; + @NonNull + final Rect mRect = new Rect(); + private boolean mIsFirstFrame = true; + private int mOverrideLayer = LAYER_NO_OVERRIDE; + + TaskFragmentAnimationAdapter(@NonNull Animation animation, + @NonNull RemoteAnimationTarget target) { + this(animation, target, target.leash, target.screenSpaceBounds); + } + + /** + * @param leash the surface to animate. + * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't + * go beyond. + */ + TaskFragmentAnimationAdapter(@NonNull Animation animation, + @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash, + @NonNull Rect wholeAnimationBounds) { + mAnimation = animation; + mTarget = target; + mLeash = leash; + mWholeAnimationBounds.set(wholeAnimationBounds); + if (target.mode == MODE_CLOSING) { + // When it is closing, we want to show the content at the start position in case the + // window is resizing as well. For example, when the activities is changing from split + // to stack, the bottom TaskFragment will be resized to fullscreen when hiding. + final Rect startBounds = target.startBounds; + final Rect endBounds = target.screenSpaceBounds; + mContentBounds.set(startBounds); + mContentRelOffset.set(target.localBounds.left, target.localBounds.top); + mContentRelOffset.offset( + startBounds.left - endBounds.left, + startBounds.top - endBounds.top); + } else { + mContentBounds.set(target.screenSpaceBounds); + mContentRelOffset.set(target.localBounds.left, target.localBounds.top); + } + } + + /** + * Surface layer to be set at the first frame of the animation. We will not set the layer if it + * is set to {@link #LAYER_NO_OVERRIDE}. + */ + final void overrideLayer(int layer) { + mOverrideLayer = layer; + } + + /** Called on frame update. */ + final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) { + if (mIsFirstFrame) { + t.show(mLeash); + if (mOverrideLayer != LAYER_NO_OVERRIDE) { + t.setLayer(mLeash, mOverrideLayer); + } + mIsFirstFrame = false; + } + + // Extract the transformation to the current time. + mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()), + mTransformation); + t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); + onAnimationUpdateInner(t); + } + + /** To be overridden by subclasses to adjust the animation surface change. */ + void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { + // Update the surface position and alpha. + mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y); + t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); + t.setAlpha(mLeash, mTransformation.getAlpha()); + + // Get current surface bounds in absolute coordinate. + // positionX/Y are in local coordinate, so minus the local offset to get the slide amount. + final int positionX = Math.round(mMatrix[MTRANS_X]); + final int positionY = Math.round(mMatrix[MTRANS_Y]); + final Rect cropRect = new Rect(mContentBounds); + cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y); + + // Store the current offset of the surface top left from (0,0) in absolute coordinate. + final int offsetX = cropRect.left; + final int offsetY = cropRect.top; + + // Intersect to make sure the animation happens within the whole animation bounds. + if (!cropRect.intersect(mWholeAnimationBounds)) { + // Hide the surface when it is outside of the animation area. + t.setAlpha(mLeash, 0); + } + + // cropRect is in absolute coordinate, so we need to translate it to surface top left. + cropRect.offset(-offsetX, -offsetY); + t.setCrop(mLeash, cropRect); + } + + /** Called after animation finished. */ + final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) { + onAnimationUpdate(t, mAnimation.getDuration()); + } + + final long getDurationHint() { + return mAnimation.computeDurationHint(); + } + + /** + * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has + * size change. + */ + static class SnapshotAdapter extends TaskFragmentAnimationAdapter { + + SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) { + // Start leash is the snapshot of the starting surface. + super(animation, target, target.startLeash, target.screenSpaceBounds); + } + + @Override + void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { + // Snapshot should always be placed at the top left of the animation leash. + mTransformation.getMatrix().postTranslate(0, 0); + t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); + t.setAlpha(mLeash, mTransformation.getAlpha()); + } + } + + /** + * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change. + */ + static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter { + + BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) { + super(animation, target); + } + + @Override + void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { + mTransformation.getMatrix().postTranslate( + mTarget.localBounds.left, mTarget.localBounds.top); + t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); + t.setAlpha(mLeash, mTransformation.getAlpha()); + + // The following applies an inverse scale to the clip-rect so that it crops "after" the + // scale instead of before. + mVecs[1] = mVecs[2] = 0; + mVecs[0] = mVecs[3] = 1; + mTransformation.getMatrix().mapVectors(mVecs); + mVecs[0] = 1.f / mVecs[0]; + mVecs[3] = 1.f / mVecs[3]; + final Rect clipRect = mTransformation.getClipRect(); + mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f); + mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f); + mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f); + mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f); + t.setWindowCrop(mLeash, mRect); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java new file mode 100644 index 000000000000..d7eb9a01f57c --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; + +import android.util.Log; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; +import android.window.TaskFragmentOrganizer; + +import androidx.annotation.NonNull; + +import com.android.internal.annotations.VisibleForTesting; + +/** Controls the TaskFragment remote animations. */ +class TaskFragmentAnimationController { + + private static final String TAG = "TaskFragAnimationCtrl"; + static final boolean DEBUG = false; + + private final TaskFragmentOrganizer mOrganizer; + private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner(); + @VisibleForTesting + final RemoteAnimationDefinition mDefinition; + private boolean mIsRegistered; + + TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) { + mOrganizer = organizer; + mDefinition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter animationAdapter = + new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter); + } + + void registerRemoteAnimations() { + if (DEBUG) { + Log.v(TAG, "registerRemoteAnimations"); + } + if (mIsRegistered) { + return; + } + mOrganizer.registerRemoteAnimations(mDefinition); + mIsRegistered = true; + } + + void unregisterRemoteAnimations() { + if (DEBUG) { + Log.v(TAG, "unregisterRemoteAnimations"); + } + if (!mIsRegistered) { + return; + } + mOrganizer.unregisterRemoteAnimations(); + mIsRegistered = false; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java new file mode 100644 index 000000000000..d9b73a8290f5 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import static android.os.Process.THREAD_PRIORITY_DISPLAY; +import static android.view.RemoteAnimationTarget.MODE_CHANGING; +import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; +import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.graphics.Rect; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.RemoteException; +import android.util.Log; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.view.animation.Animation; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +/** To run the TaskFragment animations. */ +class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub { + + private static final String TAG = "TaskFragAnimationRunner"; + private final Handler mHandler; + private final TaskFragmentAnimationSpec mAnimationSpec; + + TaskFragmentAnimationRunner() { + HandlerThread animationThread = new HandlerThread( + "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY); + animationThread.start(); + mHandler = animationThread.getThreadHandler(); + mAnimationSpec = new TaskFragmentAnimationSpec(mHandler); + } + + @Nullable + private Animator mAnimator; + + @Override + public void onAnimationStart(@WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] apps, + @NonNull RemoteAnimationTarget[] wallpapers, + @NonNull RemoteAnimationTarget[] nonApps, + @NonNull IRemoteAnimationFinishedCallback finishedCallback) { + if (wallpapers.length != 0 || nonApps.length != 0) { + throw new IllegalArgumentException("TaskFragment shouldn't handle animation with" + + "wallpaper or non-app windows."); + } + if (TaskFragmentAnimationController.DEBUG) { + Log.v(TAG, "onAnimationStart transit=" + transit); + } + mHandler.post(() -> startAnimation(transit, apps, finishedCallback)); + } + + @Override + public void onAnimationCancelled() { + mHandler.post(this::cancelAnimation); + } + + /** Creates and starts animation. */ + private void startAnimation(@WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] targets, + @NonNull IRemoteAnimationFinishedCallback finishedCallback) { + if (mAnimator != null) { + Log.w(TAG, "start new animation when the previous one is not finished yet."); + mAnimator.cancel(); + } + mAnimator = createAnimator(transit, targets, finishedCallback); + mAnimator.start(); + } + + /** Cancels animation. */ + private void cancelAnimation() { + if (mAnimator == null) { + return; + } + mAnimator.cancel(); + mAnimator = null; + } + + /** Creates the animator given the transition type and windows. */ + @NonNull + private Animator createAnimator(@WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] targets, + @NonNull IRemoteAnimationFinishedCallback finishedCallback) { + final List<TaskFragmentAnimationAdapter> adapters = + createAnimationAdapters(transit, targets); + long duration = 0; + for (TaskFragmentAnimationAdapter adapter : adapters) { + duration = Math.max(duration, adapter.getDurationHint()); + } + final ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.setDuration(duration); + animator.addUpdateListener((anim) -> { + // Update all adapters in the same transaction. + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (TaskFragmentAnimationAdapter adapter : adapters) { + adapter.onAnimationUpdate(t, animator.getCurrentPlayTime()); + } + t.apply(); + }); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) {} + + @Override + public void onAnimationEnd(Animator animation) { + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (TaskFragmentAnimationAdapter adapter : adapters) { + adapter.onAnimationEnd(t); + } + t.apply(); + + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + mAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) {} + + @Override + public void onAnimationRepeat(Animator animation) {} + }); + return animator; + } + + /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */ + @NonNull + private List<TaskFragmentAnimationAdapter> createAnimationAdapters( + @WindowManager.TransitionOldType int transit, + @NonNull RemoteAnimationTarget[] targets) { + switch (transit) { + case TRANSIT_OLD_ACTIVITY_OPEN: + case TRANSIT_OLD_TASK_FRAGMENT_OPEN: + return createOpenAnimationAdapters(targets); + case TRANSIT_OLD_ACTIVITY_CLOSE: + case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: + return createCloseAnimationAdapters(targets); + case TRANSIT_OLD_TASK_FRAGMENT_CHANGE: + return createChangeAnimationAdapters(targets); + default: + throw new IllegalArgumentException("Unhandled transit type=" + transit); + } + } + + @NonNull + private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets) { + return createOpenCloseAnimationAdapters(targets, true /* isOpening */, + mAnimationSpec::loadOpenAnimation); + } + + @NonNull + private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets) { + return createOpenCloseAnimationAdapters(targets, false /* isOpening */, + mAnimationSpec::loadCloseAnimation); + } + + /** + * Creates {@link TaskFragmentAnimationAdapter} for OPEN and CLOSE types of transition. + * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type. + */ + @NonNull + private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets, boolean isOpening, + @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) { + // We need to know if the target window is only a partial of the whole animation screen. + // If so, we will need to adjust it to make the whole animation screen looks like one. + final List<RemoteAnimationTarget> openingTargets = new ArrayList<>(); + final List<RemoteAnimationTarget> closingTargets = new ArrayList<>(); + final Rect openingWholeScreenBounds = new Rect(); + final Rect closingWholeScreenBounds = new Rect(); + for (RemoteAnimationTarget target : targets) { + if (target.mode != MODE_CLOSING) { + openingTargets.add(target); + openingWholeScreenBounds.union(target.screenSpaceBounds); + } else { + closingTargets.add(target); + closingWholeScreenBounds.union(target.screenSpaceBounds); + // Union the start bounds since this may be the ClosingChanging animation. + closingWholeScreenBounds.union(target.startBounds); + } + } + + // For OPEN transition, open windows should be above close windows. + // For CLOSE transition, open windows should be below close windows. + int offsetLayer = TYPE_LAYER_OFFSET; + final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>(); + for (RemoteAnimationTarget target : openingTargets) { + final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target, + animationProvider, openingWholeScreenBounds); + if (isOpening) { + adapter.overrideLayer(offsetLayer++); + } + adapters.add(adapter); + } + for (RemoteAnimationTarget target : closingTargets) { + final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target, + animationProvider, closingWholeScreenBounds); + if (!isOpening) { + adapter.overrideLayer(offsetLayer++); + } + adapters.add(adapter); + } + return adapters; + } + + @NonNull + private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter( + @NonNull RemoteAnimationTarget target, + @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider, + @NonNull Rect wholeAnimationBounds) { + final Animation animation = animationProvider.apply(target, wholeAnimationBounds); + return new TaskFragmentAnimationAdapter(animation, target, target.leash, + wholeAnimationBounds); + } + + @NonNull + private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters( + @NonNull RemoteAnimationTarget[] targets) { + if (shouldUseJumpCutForChangeAnimation(targets)) { + return new ArrayList<>(); + } + + final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>(); + for (RemoteAnimationTarget target : targets) { + if (target.mode == MODE_CHANGING) { + // This is the target with bounds change. + final Animation[] animations = + mAnimationSpec.createChangeBoundsChangeAnimations(target); + // Adapter for the starting snapshot leash. + adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter( + animations[0], target)); + // Adapter for the ending bounds changed leash. + adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter( + animations[1], target)); + continue; + } + + // These are the other targets that don't have bounds change in the same transition. + final Animation animation; + if (target.hasAnimatingParent) { + // No-op if it will be covered by the changing parent window. + animation = TaskFragmentAnimationSpec.createNoopAnimation(target); + } else if (target.mode == MODE_CLOSING) { + animation = mAnimationSpec.createChangeBoundsCloseAnimation(target); + } else { + animation = mAnimationSpec.createChangeBoundsOpenAnimation(target); + } + adapters.add(new TaskFragmentAnimationAdapter(animation, target)); + } + return adapters; + } + + /** + * Whether we should use jump cut for the change transition. + * This normally happens when opening a new secondary with the existing primary using a + * different split layout. This can be complicated, like from horizontal to vertical split with + * new split pairs. + * Uses a jump cut animation to simplify. + */ + private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) { + boolean hasOpeningWindow = false; + boolean hasClosingWindow = false; + for (RemoteAnimationTarget target : targets) { + if (target.hasAnimatingParent) { + continue; + } + hasOpeningWindow |= target.mode == MODE_OPENING; + hasClosingWindow |= target.mode == MODE_CLOSING; + } + return hasOpeningWindow && hasClosingWindow; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java new file mode 100644 index 000000000000..1f866c3b99c9 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.window.extensions.embedding; + +import static android.view.RemoteAnimationTarget.MODE_CLOSING; + +import android.app.ActivityThread; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.Rect; +import android.os.Handler; +import android.provider.Settings; +import android.view.RemoteAnimationTarget; +import android.view.WindowManager; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.AnimationUtils; +import android.view.animation.ClipRectAnimation; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.ScaleAnimation; +import android.view.animation.TranslateAnimation; + +import androidx.annotation.NonNull; + +import com.android.internal.R; +import com.android.internal.policy.AttributeCache; +import com.android.internal.policy.TransitionAnimation; + +/** Animation spec for TaskFragment transition. */ +// TODO(b/206557124): provide an easier way to customize animation +class TaskFragmentAnimationSpec { + + private static final String TAG = "TaskFragAnimationSpec"; + private static final int CHANGE_ANIMATION_DURATION = 517; + private static final int CHANGE_ANIMATION_FADE_DURATION = 80; + private static final int CHANGE_ANIMATION_FADE_OFFSET = 30; + + private final Context mContext; + private final TransitionAnimation mTransitionAnimation; + private final Interpolator mFastOutExtraSlowInInterpolator; + private final LinearInterpolator mLinearInterpolator; + private float mTransitionAnimationScaleSetting; + + TaskFragmentAnimationSpec(@NonNull Handler handler) { + mContext = ActivityThread.currentActivityThread().getApplication(); + mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG); + // Initialize the AttributeCache for the TransitionAnimation. + AttributeCache.init(mContext); + mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.fast_out_extra_slow_in); + mLinearInterpolator = new LinearInterpolator(); + + // The transition animation should be adjusted based on the developer option. + final ContentResolver resolver = mContext.getContentResolver(); + mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting(); + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false, + new SettingsObserver(handler)); + } + + /** For target that doesn't need to be animated. */ + @NonNull + static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) { + // Noop but just keep the target showing/hiding. + final float alpha = target.mode == MODE_CLOSING ? 0f : 1f; + return new AlphaAnimation(alpha, alpha); + } + + /** Animation for target that is opening in a change transition. */ + @NonNull + Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) { + final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); + final Rect bounds = target.screenSpaceBounds; + final int startLeft; + final int startTop; + if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) { + // The window will be animated in from left or right depending on its position. + startTop = 0; + startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width(); + } else { + // The window will be animated in from top or bottom depending on its position. + startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height(); + startLeft = 0; + } + + // The position should be 0-based as we will post translate in + // TaskFragmentAnimationAdapter#onAnimationUpdate + final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0); + animation.setInterpolator(mFastOutExtraSlowInInterpolator); + animation.setDuration(CHANGE_ANIMATION_DURATION); + animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + /** Animation for target that is closing in a change transition. */ + @NonNull + Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) { + final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); + // Use startBounds if the window is closing in case it may also resize. + final Rect bounds = target.startBounds; + final int endTop; + final int endLeft; + if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) { + // The window will be animated out to left or right depending on its position. + endTop = 0; + endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width(); + } else { + // The window will be animated out to top or bottom depending on its position. + endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height(); + endLeft = 0; + } + + // The position should be 0-based as we will post translate in + // TaskFragmentAnimationAdapter#onAnimationUpdate + final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop); + animation.setInterpolator(mFastOutExtraSlowInInterpolator); + animation.setDuration(CHANGE_ANIMATION_DURATION); + animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + /** + * Animation for target that is changing (bounds change) in a change transition. + * @return the return array always has two elements. The first one is for the start leash, and + * the second one is for the end leash. + */ + @NonNull + Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) { + // Both start bounds and end bounds are in screen coordinates. We will post translate + // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate + final Rect startBounds = target.startBounds; + final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds(); + final Rect endBounds = target.screenSpaceBounds; + float scaleX = ((float) startBounds.width()) / endBounds.width(); + float scaleY = ((float) startBounds.height()) / endBounds.height(); + // Start leash is a child of the end leash. Reverse the scale so that the start leash won't + // be scaled up with its parent. + float startScaleX = 1.f / scaleX; + float startScaleY = 1.f / scaleY; + + // The start leash will be fade out. + final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */); + final Animation startAlpha = new AlphaAnimation(1f, 0f); + startAlpha.setInterpolator(mLinearInterpolator); + startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION); + startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET); + startSet.addAnimation(startAlpha); + final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY, + startScaleY); + startScale.setInterpolator(mFastOutExtraSlowInInterpolator); + startScale.setDuration(CHANGE_ANIMATION_DURATION); + startSet.addAnimation(startScale); + startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(), + endBounds.height()); + startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting); + + // The end leash will be moved into the end position while scaling. + final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */); + endSet.setInterpolator(mFastOutExtraSlowInInterpolator); + final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1); + endScale.setDuration(CHANGE_ANIMATION_DURATION); + endSet.addAnimation(endScale); + // The position should be 0-based as we will post translate in + // TaskFragmentAnimationAdapter#onAnimationUpdate + final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0, + startBounds.top - endBounds.top, 0); + endTranslate.setDuration(CHANGE_ANIMATION_DURATION); + endSet.addAnimation(endTranslate); + // The end leash is resizing, we should update the window crop based on the clip rect. + final Rect startClip = new Rect(startBounds); + final Rect endClip = new Rect(endBounds); + startClip.offsetTo(0, 0); + endClip.offsetTo(0, 0); + final Animation clipAnim = new ClipRectAnimation(startClip, endClip); + clipAnim.setDuration(CHANGE_ANIMATION_DURATION); + endSet.addAnimation(clipAnim); + endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(), + parentBounds.height()); + endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting); + + return new Animation[]{startSet, endSet}; + } + + @NonNull + Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target, + @NonNull Rect wholeAnimationBounds) { + final boolean isEnter = target.mode != MODE_CLOSING; + final Animation animation; + // Background color on TaskDisplayArea has already been set earlier in + // WindowContainer#getAnimationAdapter. + if (target.showBackdrop) { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_clear_top_open_enter + : com.android.internal.R.anim.task_fragment_clear_top_open_exit); + } else { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_open_enter + : com.android.internal.R.anim.task_fragment_open_exit); + } + // Use the whole animation bounds instead of the change bounds, so that when multiple change + // targets are opening at the same time, the animation applied to each will be the same. + // Otherwise, we may see gap between the activities that are launching together. + animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(), + wholeAnimationBounds.width(), wholeAnimationBounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + @NonNull + Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target, + @NonNull Rect wholeAnimationBounds) { + final boolean isEnter = target.mode != MODE_CLOSING; + final Animation animation; + if (target.showBackdrop) { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_clear_top_close_enter + : com.android.internal.R.anim.task_fragment_clear_top_close_exit); + } else { + animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter + ? com.android.internal.R.anim.task_fragment_close_enter + : com.android.internal.R.anim.task_fragment_close_exit); + } + // Use the whole animation bounds instead of the change bounds, so that when multiple change + // targets are closing at the same time, the animation applied to each will be the same. + // Otherwise, we may see gap between the activities that are finishing together. + animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(), + wholeAnimationBounds.width(), wholeAnimationBounds.height()); + animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); + return animation; + } + + private float getTransitionAnimationScaleSetting() { + return WindowManager.fixScale(Settings.Global.getFloat(mContext.getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat( + R.dimen.config_appTransitionAnimationDurationScaleDefault))); + } + + private class SettingsObserver extends ContentObserver { + SettingsObserver(@NonNull Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting(); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index dc6506b070af..dc1d983997c6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -53,14 +53,12 @@ import java.util.Objects; class TaskFragmentContainer { private static final int APPEAR_EMPTY_TIMEOUT_MS = 3000; + /** Parcelable data of this TaskFragmentContainer. */ @NonNull - private final SplitController mController; + private final ParcelableTaskFragmentContainerData mParcelableData; - /** - * Client-created token that uniquely identifies the task fragment container instance. - */ @NonNull - private final IBinder mToken; + private final SplitController mController; /** Parent leaf Task. */ @NonNull @@ -103,9 +101,6 @@ class TaskFragmentContainer { */ private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>(); - @Nullable - private final String mOverlayTag; - /** * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()} * for {@link #isOverlay()} container. @@ -113,29 +108,13 @@ class TaskFragmentContainer { @NonNull private final Bundle mLaunchOptions = new Bundle(); - /** - * The associated {@link Activity#getActivityToken()} of the overlay container. - * Must be {@code null} for non-overlay container. - * <p> - * If an overlay container is associated with an activity, this overlay container will be - * dismissed when the associated activity is destroyed. If the overlay container is visible, - * activity will be launched on top of the overlay container and expanded to fill the parent - * container. - */ - @Nullable - private final IBinder mAssociatedActivityToken; - /** Indicates whether the container was cleaned up after the last activity was removed. */ private boolean mIsFinished; /** - * Bounds that were requested last via {@link android.window.WindowContainerTransaction}. - */ - private final Rect mLastRequestedBounds = new Rect(); - - /** * Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}. */ + // TODO(b/289875940): review this and other field that might need to be moved in the base class. @WindowingMode private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED; @@ -208,17 +187,17 @@ class TaskFragmentContainer { @NonNull SplitController controller, @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag, @Nullable Bundle launchOptions, @Nullable Activity associatedActivity) { + mParcelableData = new ParcelableTaskFragmentContainerData( + new Binder("TaskFragmentContainer"), overlayTag, + associatedActivity != null ? associatedActivity.getActivityToken() : null); + if ((pendingAppearedActivity == null && pendingAppearedIntent == null) || (pendingAppearedActivity != null && pendingAppearedIntent != null)) { throw new IllegalArgumentException( "One and only one of pending activity and intent must be non-null"); } mController = controller; - mToken = new Binder("TaskFragmentContainer"); mTaskContainer = taskContainer; - mOverlayTag = overlayTag; - mAssociatedActivityToken = associatedActivity != null - ? associatedActivity.getActivityToken() : null; if (launchOptions != null) { mLaunchOptions.putAll(launchOptions); @@ -259,18 +238,26 @@ class TaskFragmentContainer { if (overlayTag != null && pendingAppearedIntent != null && associatedActivity != null && !associatedActivity.isFinishing()) { final IBinder associatedActivityToken = associatedActivity.getActivityToken(); - final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken, - launchOptions, pendingAppearedIntent); + final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams( + mParcelableData.mToken, launchOptions, pendingAppearedIntent); mController.mOverlayRestoreParams.put(associatedActivityToken, params); } } + /** This is only used when restoring it from a {@link ParcelableTaskFragmentContainerData}. */ + TaskFragmentContainer(@NonNull ParcelableTaskFragmentContainerData data, + @NonNull SplitController splitController, @NonNull TaskContainer taskContainer) { + mParcelableData = data; + mController = splitController; + mTaskContainer = taskContainer; + } + /** * Returns the client-created token that uniquely identifies this container. */ @NonNull IBinder getTaskFragmentToken() { - return mToken; + return mParcelableData.mToken; } /** List of non-finishing activities that belong to this container and live in this process. */ @@ -389,7 +376,8 @@ class TaskFragmentContainer { return null; } return new ActivityStack(activities, isEmpty(), - ActivityStack.Token.createFromBinder(mToken), mOverlayTag); + ActivityStack.Token.createFromBinder(mParcelableData.mToken), + mParcelableData.mOverlayTag); } /** Adds the activity that will be reparented to this container. */ @@ -413,7 +401,7 @@ class TaskFragmentContainer { final ActivityThread.ActivityClientRecord record = ActivityThread .currentActivityThread().getActivityClient(activityToken); if (record != null) { - record.mTaskFragmentToken = mToken; + record.mTaskFragmentToken = mParcelableData.mToken; } } @@ -469,7 +457,7 @@ class TaskFragmentContainer { if (!isOverlayWithActivityAssociation()) { return; } - if (mAssociatedActivityToken == activityToken) { + if (mParcelableData.mAssociatedActivityToken == activityToken) { // If the associated activity is destroyed, also finish this overlay container. mController.mPresenter.cleanupContainer(wct, this, false /* shouldFinishDependent */); } @@ -776,8 +764,8 @@ class TaskFragmentContainer { * @see WindowContainerTransaction#setRelativeBounds */ boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) { - return (relBounds == null && mLastRequestedBounds.isEmpty()) - || mLastRequestedBounds.equals(relBounds); + return (relBounds == null && mParcelableData.mLastRequestedBounds.isEmpty()) + || mParcelableData.mLastRequestedBounds.equals(relBounds); } /** @@ -787,14 +775,14 @@ class TaskFragmentContainer { */ void setLastRequestedBounds(@Nullable Rect relBounds) { if (relBounds == null) { - mLastRequestedBounds.setEmpty(); + mParcelableData.mLastRequestedBounds.setEmpty(); } else { - mLastRequestedBounds.set(relBounds); + mParcelableData.mLastRequestedBounds.set(relBounds); } } @NonNull Rect getLastRequestedBounds() { - return mLastRequestedBounds; + return mParcelableData.mLastRequestedBounds; } /** @@ -965,6 +953,16 @@ class TaskFragmentContainer { return mTaskContainer.getTaskId(); } + @NonNull + IBinder getToken() { + return mParcelableData.mToken; + } + + @NonNull + ParcelableTaskFragmentContainerData getParcelableData() { + return mParcelableData; + } + /** Gets the parent Task. */ @NonNull TaskContainer getTaskContainer() { @@ -1011,7 +1009,7 @@ class TaskFragmentContainer { /** Returns whether this taskFragment container is an overlay container. */ boolean isOverlay() { - return mOverlayTag != null; + return mParcelableData.mOverlayTag != null; } /** @@ -1020,7 +1018,7 @@ class TaskFragmentContainer { */ @Nullable String getOverlayTag() { - return mOverlayTag; + return mParcelableData.mOverlayTag; } /** @@ -1045,7 +1043,7 @@ class TaskFragmentContainer { */ @Nullable IBinder getAssociatedActivityToken() { - return mAssociatedActivityToken; + return mParcelableData.mAssociatedActivityToken; } /** @@ -1053,11 +1051,11 @@ class TaskFragmentContainer { * a non-fill-parent overlay without activity association. */ boolean isAlwaysOnTopOverlay() { - return isOverlay() && mAssociatedActivityToken == null; + return isOverlay() && mParcelableData.mAssociatedActivityToken == null; } boolean isOverlayWithActivityAssociation() { - return isOverlay() && mAssociatedActivityToken != null; + return isOverlay() && mParcelableData.mAssociatedActivityToken != null; } @Override @@ -1074,13 +1072,13 @@ class TaskFragmentContainer { private String toString(boolean includeContainersToFinishOnExit) { return "TaskFragmentContainer{" + " parentTaskId=" + getTaskId() - + " token=" + mToken + + " token=" + mParcelableData.mToken + " topNonFinishingActivity=" + getTopNonFinishingActivity() + " runningActivityCount=" + getRunningActivityCount() + " isFinished=" + mIsFinished - + " overlayTag=" + mOverlayTag - + " associatedActivityToken=" + mAssociatedActivityToken - + " lastRequestedBounds=" + mLastRequestedBounds + + " overlayTag=" + mParcelableData.mOverlayTag + + " associatedActivityToken=" + mParcelableData.mAssociatedActivityToken + + " lastRequestedBounds=" + mParcelableData.mLastRequestedBounds + " pendingAppearedActivities=" + mPendingAppearedActivities + (includeContainersToFinishOnExit ? " containersToFinishOnExit=" + containersToFinishOnExitToString() : "") diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 8911d18b9b97..ac004c301598 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -23,6 +23,8 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTest import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -84,6 +86,24 @@ public class JetpackTaskFragmentOrganizerTest { } @Test + public void testUnregisterOrganizer() { + mOrganizer.overrideSplitAnimation(); + mOrganizer.unregisterOrganizer(); + + verify(mOrganizer).unregisterRemoteAnimations(); + } + + @Test + public void testOverrideSplitAnimation() { + assertNull(mOrganizer.mAnimationController); + + mOrganizer.overrideSplitAnimation(); + + assertNotNull(mOrganizer.mAnimationController); + verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition); + } + + @Test public void testExpandTaskFragment() { final TaskContainer taskContainer = createTestTaskContainer(); doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt()); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java new file mode 100644 index 000000000000..a1e9f08585f6 --- /dev/null +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 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 androidx.window.extensions.embedding; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.Mockito.never; + +import android.platform.test.annotations.Presubmit; +import android.window.TaskFragmentOrganizer; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +/** + * Test class for {@link TaskFragmentAnimationController}. + * + * Build/Install/Run: + * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest + */ +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TaskFragmentAnimationControllerTest { + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Mock + private TaskFragmentOrganizer mOrganizer; + private TaskFragmentAnimationController mAnimationController; + + @Before + public void setup() { + mAnimationController = new TaskFragmentAnimationController(mOrganizer); + } + + @Test + public void testRegisterRemoteAnimations() { + mAnimationController.registerRemoteAnimations(); + + verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition); + + mAnimationController.registerRemoteAnimations(); + + // No extra call if it has been registered. + verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition); + } + + @Test + public void testUnregisterRemoteAnimations() { + mAnimationController.unregisterRemoteAnimations(); + + // No call if it is not registered. + verify(mOrganizer, never()).unregisterRemoteAnimations(); + + mAnimationController.registerRemoteAnimations(); + mAnimationController.unregisterRemoteAnimations(); + + verify(mOrganizer).unregisterRemoteAnimations(); + + mAnimationController.unregisterRemoteAnimations(); + + // No extra call if it has been unregistered. + verify(mOrganizer).unregisterRemoteAnimations(); + } +} 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 90b9798c6329..cbd4a528474a 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 @@ class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { 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 ed2a0a718c6c..578a9b536289 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 @@ open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipT @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 8cb81b46cf4d..f57335c2081b 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 @@ abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(fl } } + @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 0742cf9c5887..ce84eb644042 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 @@ abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransitio } } + @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 99c1ad2aaa4e..bc2bfdbe1df1 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 @@ package com.android.wm.shell.flicker.pip.common 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 @@ abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) { .doesNotContain(false) } } + + @Postsubmit + @Test + open fun pipLayerHasCorrectCornersAtEnd() { + flicker.assertLayersEnd { hasRoundedCorners(pipApp) } + } } diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index 6c0f93354683..e2ec95215d1a 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -62,7 +62,7 @@ interface INfcAdapter void dispatch(in Tag tag); - void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras); + void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras, String pkg); void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList); void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler); @@ -100,7 +100,7 @@ interface INfcAdapter void unregisterWlcStateListener(in INfcWlcStateListener listener); WlcListenerDeviceInfo getWlcListenerDeviceInfo(); - void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags); + void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags, String pkg); void notifyPollingLoop(in PollingFrame frame); void notifyHceDeactivated(); diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java index 0eb846d6c72a..909eca7b0c9d 100644 --- a/nfc/java/android/nfc/NfcActivityManager.java +++ b/nfc/java/android/nfc/NfcActivityManager.java @@ -236,7 +236,8 @@ public final class NfcActivityManager extends IAppCallback.Stub public void setReaderMode(Binder token, int flags, Bundle extras) { if (DBG) Log.d(TAG, "Setting reader mode"); - NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(token, this, flags, extras)); + NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode( + token, this, flags, extras, mAdapter.getContext().getPackageName())); } /** @@ -395,7 +396,8 @@ public final class NfcActivityManager extends IAppCallback.Stub private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) { NfcAdapter.callService( - () -> NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech)); + () -> NfcAdapter.sService.updateDiscoveryTechnology( + token, pollTech, listenTech, mAdapter.getContext().getPackageName())); } } diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index 525e2c58a82c..22ae612b5cfc 100644 --- a/nfc/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java @@ -1731,7 +1731,8 @@ public final class NfcAdapter { } Binder token = new Binder(); int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS; - callService(() -> sService.setReaderMode(token, null, flags, null)); + callService(() -> sService.setReaderMode( + token, null, flags, null, mContext.getPackageName())); } /** @@ -1804,7 +1805,8 @@ public final class NfcAdapter { || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) { Binder token = new Binder(); callService( () -> - sService.updateDiscoveryTechnology(token, pollTechnology, listenTechnology)); + sService.updateDiscoveryTechnology( + token, pollTechnology, listenTechnology, mContext.getPackageName())); } else { mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology); } 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 8a25726ecd17..0d4ef3c52e4c 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 7e626e50255a..307277264ff6 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 9f4980beb76f..f1790f9ba351 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 67b5107576a7..f0da7b4f457f 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 055afedd9422..0ffb76307b3c 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 @@ public class BluetoothUtils { 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 26905b1d86d2..6f2567b9c5dc 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 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { 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/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java index fe0f98af1186..43c6c5050850 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java @@ -31,7 +31,6 @@ import android.util.LruCache; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.ListenableFuture; @@ -53,25 +52,22 @@ public class ZenIconLoader { private final LruCache<ZenIcon.Key, Drawable> mCache; private final ListeningExecutorService mBackgroundExecutor; + /** Obtains the singleton {@link ZenIconLoader}. */ public static ZenIconLoader getInstance() { if (sInstance == null) { - sInstance = new ZenIconLoader(); + sInstance = new ZenIconLoader(Executors.newFixedThreadPool(4)); } return sInstance; } - /** Replaces the singleton instance of {@link ZenIconLoader} by the provided one. */ - @VisibleForTesting(otherwise = VisibleForTesting.NONE) - public static void setInstance(@Nullable ZenIconLoader instance) { - sInstance = instance; - } - - private ZenIconLoader() { - this(Executors.newFixedThreadPool(4)); - } - - @VisibleForTesting - public ZenIconLoader(ExecutorService backgroundExecutor) { + /** + * Constructs a ZenIconLoader with the specified {@code backgroundExecutor}. + * + * <p>ZenIconLoader <em>should be a singleton</em>, so this should only be used to instantiate + * and provide the singleton instance in a module. If the app doesn't support dependency + * injection, use {@link #getInstance} instead. + */ + public ZenIconLoader(@NonNull ExecutorService backgroundExecutor) { mCache = new LruCache<>(50); mBackgroundExecutor = MoreExecutors.listeningDecorator(backgroundExecutor); 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 3a7b0c7bb104..a0e764ad3d5d 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 @@ public class BluetoothUtilsTest { + "</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 @@ public class BluetoothUtilsTest { } @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 c1bb55c70073..d26a9066e075 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -949,36 +949,37 @@ android_robolectric_test { 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 8a1d81be5e11..97206de346c5 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -139,13 +139,6 @@ flag { } 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 { } 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 { } 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 { } 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 7707a600c691..fe9105ed195e 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.util.LatencyTracker 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.never 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 @@ import com.android.systemui.Flags as AconfigFlags 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 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { 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 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { 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 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { 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 f7f69d373959..ca585abf5100 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.flags.Flags 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 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() { 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 d244482c05ee..58c3fec5b45e 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 @@ package com.android.systemui.ambient.touch; 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.ext.junit.runners.AndroidJUnit4; 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.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.Collections; import java.util.Optional; @SmallTest @@ -106,15 +106,13 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase { 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 class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase { public void setup() { mKosmos = new KosmosJavaAdapter(this); MockitoAnnotations.initMocks(this); - mUserTracker = new FakeUserTracker(); mTouchHandler = new BouncerSwipeTouchHandler( mKosmos.getTestScope(), mScrimManager, @@ -138,24 +135,21 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase { 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 b85e32b381df..95681671b545 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 @@ package com.android.systemui.ambient.touch; 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.ext.junit.runners.AndroidJUnit4; 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.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.Collections; import java.util.Optional; @SmallTest @@ -116,9 +116,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { UiEventLogger mUiEventLogger; @Mock - LockPatternUtils mLockPatternUtils; - - @Mock ActivityStarter mActivityStarter; @Mock @@ -127,11 +124,12 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { @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 class BouncerSwipeTouchHandlerTest extends SysuiTestCase { public void setup() { mKosmos = new KosmosJavaAdapter(this); MockitoAnnotations.initMocks(this); - mUserTracker = new FakeUserTracker(); mTouchHandler = new BouncerSwipeTouchHandler( mKosmos.getTestScope(), mScrimManager, @@ -156,24 +153,21 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { 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 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { */ @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 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { */ @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 a068118051f9..d21a8270ae54 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.MockitoAnnotations 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 @@ class DreamOverlayServiceTest : SysuiTestCase() { } @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 3b8ffcd91c48..17e3006812ca 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 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { 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 6eb9862fb4f1..33f3cd4ac167 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.ext.junit.runners.AndroidJUnit4 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 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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { 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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { 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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { 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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { 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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @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 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { 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 ee4a0d2d4e75..c18deb134075 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.data.repository.fakeCommunalSceneRepository 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 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 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @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 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT 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 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @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 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT 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 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT @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 @@ class FromDozingTransitionInteractorTest(flags: FlagsParameterization?) : SysuiT 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 6e76cbce95f1..6708ffa2a091 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 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() { } @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 3e0a1f31302d..073ed61a949b 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 @@ package com.android.systemui.keyguard.domain.interactor 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 @@ class LightRevealScrimInteractorTest : SysuiTestCase() { @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/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt index 5925819f27a7..5fd3a242e195 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt @@ -24,9 +24,10 @@ 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.settingslib.notification.modes.ZenIconLoader +import com.android.internal.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.asIcon +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope @@ -36,7 +37,6 @@ import com.android.systemui.statusbar.policy.data.repository.fakeZenModeReposito import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat -import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.toCollection @@ -60,10 +60,10 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @Before fun setUp() { context.orCreateTestableResources.apply { - addOverride(com.android.internal.R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE) - addOverride(com.android.internal.R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE) + addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE) + addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE) + addOverride(R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE) } - ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) } @EnableFlags(Flags.FLAG_MODES_UI) @@ -128,28 +128,34 @@ class ModesTileDataInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) - fun changesIconWhenActiveModesChange() = + fun tileData_iconsFlagEnabled_changesIconWhenActiveModesChange() = testScope.runTest { - val dataList: List<ModesTileModel> by - collectValues( + val tileData by + collectLastValue( underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)) ) + + // Tile starts with the generic Modes icon. runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null).inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) - // Add an inactive mode: state hasn't changed, so this shouldn't cause another emission + // Add an inactive mode -> Still modes icon zenModeRepository.addMode(id = "Mode", active = false) runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null).inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) - // Add an active mode: icon should be the mode icon + // Add an active mode: icon should be the mode icon. No iconResId, because we don't + // really know that it's a system icon. zenModeRepository.addMode( id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME, active = true ) runCurrent() - assertThat(dataList.map { it.icon }).containsExactly(null, BEDTIME_ICON).inOrder() + assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON) + assertThat(tileData?.iconResId).isNull() // Add another, less-prioritized mode: icon should remain the first mode icon zenModeRepository.addMode( @@ -158,29 +164,58 @@ class ModesTileDataInteractorTest : SysuiTestCase() { active = true ) runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON) - .inOrder() + assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON) + assertThat(tileData?.iconResId).isNull() - // Deactivate more important mode: icon should be the less important, still active mode. + // Deactivate more important mode: icon should be the less important, still active mode zenModeRepository.deactivateMode("Bedtime") runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON) - .inOrder() + assertThat(tileData?.icon).isEqualTo(DRIVING_ICON) + assertThat(tileData?.iconResId).isNull() - // Deactivate remaining mode: no icon + // Deactivate remaining mode: back to the default modes icon zenModeRepository.deactivateMode("Driving") runCurrent() - assertThat(dataList.map { it.icon }) - .containsExactly(null, BEDTIME_ICON, BEDTIME_ICON, DRIVING_ICON, null) - .inOrder() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun tileData_iconsFlagDisabled_hasPriorityModesIcon() = + testScope.runTest { + val tileData by + collectLastValue( + underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest)) + ) + + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + + // Activate a Mode -> Icon doesn't change. + zenModeRepository.addMode(id = "Mode", active = true) + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) + + zenModeRepository.deactivateMode(id = "Mode") + runCurrent() + assertThat(tileData?.icon).isEqualTo(MODES_ICON) + assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID) } private companion object { val TEST_USER = UserHandle.of(1)!! + + val MODES_DRAWABLE_ID = com.android.systemui.res.R.drawable.qs_dnd_icon_off + + val MODES_DRAWABLE = TestStubDrawable("modes_icon") val BEDTIME_DRAWABLE = TestStubDrawable("bedtime") val DRIVING_DRAWABLE = TestStubDrawable("driving") + + val MODES_ICON = MODES_DRAWABLE.asIcon() val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon() val DRIVING_ICON = DRIVING_DRAWABLE.asIcon() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt index 4b7564981855..cd5812710292 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor +import android.graphics.drawable.TestStubDrawable import android.platform.test.annotations.EnableFlags import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx @@ -54,10 +56,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { fun handleClick_active() = runTest { val expandable = mock<Expandable>() underTest.handleInput( - QSTileInputTestKtx.click( - data = ModesTileModel(true, listOf("DND")), - expandable = expandable - ) + QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable) ) verify(mockDialogDelegate).showDialog(eq(expandable)) @@ -67,10 +66,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { fun handleClick_inactive() = runTest { val expandable = mock<Expandable>() underTest.handleInput( - QSTileInputTestKtx.click( - data = ModesTileModel(false, emptyList()), - expandable = expandable - ) + QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable) ) verify(mockDialogDelegate).showDialog(eq(expandable)) @@ -78,7 +74,7 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick_active() = runTest { - underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true, listOf("DND")))) + underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND")))) QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) @@ -87,10 +83,14 @@ class ModesTileUserActionInteractorTest : SysuiTestCase() { @Test fun handleLongClick_inactive() = runTest { - underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false, emptyList()))) + underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList()))) QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS) } } + + private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel { + return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt index a41f15d8ff82..f7bdcb8086ef 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt @@ -18,11 +18,12 @@ package com.android.systemui.qs.tiles.impl.modes.ui import android.app.Flags import android.graphics.drawable.TestStubDrawable +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.systemui.SysuiTestCase -import com.android.systemui.common.shared.model.Icon +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder import com.android.systemui.qs.tiles.viewmodel.QSTileState @@ -58,47 +59,88 @@ class ModesTileMapperTest : SysuiTestCase() { @Test fun inactiveState() { - val model = ModesTileModel(isActivated = false, activeModes = emptyList()) + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_off) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("No active modes") } @Test fun activeState_oneMode() { - val model = ModesTileModel(isActivated = true, activeModes = listOf("DND")) + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = true, + activeModes = listOf("DND"), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("DND is active") } @Test fun activeState_multipleModes() { + val icon = TestStubDrawable("res123").asIcon() val model = - ModesTileModel(isActivated = true, activeModes = listOf("Mode 1", "Mode 2", "Mode 3")) + ModesTileModel( + isActivated = true, + activeModes = listOf("Mode 1", "Mode 2", "Mode 3"), + icon = icon, + ) val state = underTest.map(config, model) assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE) - assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.secondaryLabel).isEqualTo("3 modes are active") } @Test @EnableFlags(Flags.FLAG_MODES_UI_ICONS) - fun activeState_withIcon() { - val icon = Icon.Resource(1234, contentDescription = null) - val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"), icon = icon) + fun state_withEnabledFlag_noIconResId() { + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + iconResId = 123 // Should not be populated, but is ignored even if present + ) val state = underTest.map(config, model) + assertThat(state.icon()).isEqualTo(icon) assertThat(state.iconRes).isNull() + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun state_withDisabledFlag_includesIconResId() { + val icon = TestStubDrawable("res123").asIcon() + val model = + ModesTileModel( + isActivated = false, + activeModes = emptyList(), + icon = icon, + iconResId = 123 + ) + + val state = underTest.map(config, model) + assertThat(state.icon()).isEqualTo(icon) + assertThat(state.iconRes).isEqualTo(123) } } 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 45a8b3e968cd..db58c8500768 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.ExperimentalCoroutinesApi 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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { 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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { 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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { 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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @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 @@ class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() { @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 dd4432dd9797..900f2a464588 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 com.google.common.truth.Truth.assertThat 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 @@ class SceneActionsViewModelTest : SysuiTestCase() { 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 14134ccc34d0..cea8857c01bf 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.kosmos.testScope 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 @@ import org.junit.runner.RunWith @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 f96cf1011fb8..840aa92548c8 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 androidx.test.filters.SmallTest 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.FakeHeadsUpRo 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 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun pinnedHeadsUpRows_filtersForPinnedItems() = testScope.runTest { val pinnedHeadsUpRows by collectLastValue(underTest.pinnedHeadsUpRows) @@ -576,7 +576,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun hasPinnedHeadsUpRows_true() = testScope.runTest { val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow) @@ -591,7 +591,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun hasPinnedHeadsUpRows_false() = testScope.runTest { val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow) @@ -606,7 +606,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun topHeadsUpRow_emptyList_null() = testScope.runTest { val topHeadsUpRow by collectLastValue(underTest.topHeadsUpRow) @@ -618,7 +618,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer fun headsUpAnimationsEnabled_true() = testScope.runTest { val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled) @@ -631,7 +631,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas } @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 ca106fa01f9a..469a7bc6d9fb 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 android.testing.TestableLooper.RunWithLooper 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.provider.VisualSta 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 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager @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/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt index 20d3a7b6b7b5..639d34d5e74d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt @@ -22,8 +22,10 @@ import android.provider.Settings import android.provider.Settings.Secure.ZEN_DURATION import android.provider.Settings.Secure.ZEN_DURATION_FOREVER import android.provider.Settings.Secure.ZEN_DURATION_PROMPT +import android.service.notification.SystemZenRules import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.R import com.android.settingslib.notification.data.repository.updateNotificationPolicy import com.android.settingslib.notification.modes.TestModeBuilder import com.android.systemui.SysuiTestCase @@ -220,30 +222,86 @@ class ZenModeInteractorTest : SysuiTestCase() { } @Test - fun mainActiveMode_returnsMainActiveMode() = + fun activeModes_computesMainActiveMode() = testScope.runTest { - val mainActiveMode by collectLastValue(underTest.mainActiveMode) + val activeModes by collectLastValue(underTest.activeModes) zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME) zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER) runCurrent() + assertThat(activeModes?.modeNames).hasSize(0) + assertThat(activeModes?.mainMode).isNull() + + zenModeRepository.activateMode("Other") + runCurrent() + assertThat(activeModes?.modeNames).containsExactly("Mode Other") + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Other") + + zenModeRepository.activateMode("Bedtime") + runCurrent() + assertThat(activeModes?.modeNames) + .containsExactly("Mode Bedtime", "Mode Other") + .inOrder() + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime") + + zenModeRepository.deactivateMode("Other") + runCurrent() + assertThat(activeModes?.modeNames).containsExactly("Mode Bedtime") + assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime") + + zenModeRepository.deactivateMode("Bedtime") + runCurrent() + assertThat(activeModes?.modeNames).hasSize(0) + assertThat(activeModes?.mainMode).isNull() + } + + @Test + fun mainActiveMode_flows() = + testScope.runTest { + val mainActiveMode by collectLastValue(underTest.mainActiveMode) + + zenModeRepository.addModes( + listOf( + TestModeBuilder() + .setId("Bedtime") + .setName("Mode Bedtime") + .setType(AutomaticZenRule.TYPE_BEDTIME) + .setActive(false) + .setPackage(mContext.packageName) + .setIconResId(R.drawable.ic_zen_mode_type_bedtime) + .build(), + TestModeBuilder() + .setId("Other") + .setName("Mode Other") + .setType(AutomaticZenRule.TYPE_OTHER) + .setActive(false) + .setPackage(SystemZenRules.PACKAGE_ANDROID) + .setIconResId(R.drawable.ic_zen_mode_type_other) + .build(), + ) + ) + + runCurrent() assertThat(mainActiveMode).isNull() zenModeRepository.activateMode("Other") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Other") + assertThat(mainActiveMode?.name).isEqualTo("Mode Other") + assertThat(mainActiveMode?.icon?.key?.resId) + .isEqualTo(R.drawable.ic_zen_mode_type_other) zenModeRepository.activateMode("Bedtime") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Bedtime") + assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime") + assertThat(mainActiveMode?.icon?.key?.resId) + .isEqualTo(R.drawable.ic_zen_mode_type_bedtime) zenModeRepository.deactivateMode("Other") runCurrent() - assertThat(mainActiveMode).isNotNull() - assertThat(mainActiveMode!!.id).isEqualTo("Bedtime") + assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime") + assertThat(mainActiveMode?.icon?.key?.resId) + .isEqualTo(R.drawable.ic_zen_mode_type_bedtime) zenModeRepository.deactivateMode("Bedtime") runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt index 33f379d020b7..d2bc54e09944 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelTest.kt @@ -23,7 +23,6 @@ import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.notification.modes.TestModeBuilder -import com.android.settingslib.notification.modes.ZenIconLoader import com.android.settingslib.notification.modes.ZenMode import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue @@ -35,12 +34,10 @@ import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat -import com.google.common.util.concurrent.MoreExecutors import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations @@ -67,11 +64,6 @@ class ModesDialogViewModelTest : SysuiTestCase() { mockDialogEventLogger ) - @Before - fun setUp() { - ZenIconLoader.setInstance(ZenIconLoader(MoreExecutors.newDirectExecutorService())) - } - @Test fun tiles_filtersOutUserDisabledModes() = testScope.runTest { 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 51a70bda6034..fe6c7417032f 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.dump.DumpManager 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 @@ class VolumePanelViewModelTest : SysuiTestCase() { @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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e6cc6cf766c6..fdba2e67664f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -270,6 +270,7 @@ <!-- Add to note button used in App Clips flow to return the saved screenshot image to notes app. [CHAR LIMIT=NONE] --> <string name="app_clips_save_add_to_note">Add to note</string> <string name="backlinks_include_link">Include link</string> + <string name="backlinks_duplicate_label_format"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> <xliff:g id="frequencyCount" example="(1)">(%2$d)</xliff:g></string> <!-- Notification title displayed for screen recording [CHAR LIMIT=50]--> <string name="screenrecord_title">Screen Recorder</string> 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 64fe78d9958b..7ec977a8d6aa 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 @@ public class Task { @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 490ad5c4136d..3ad73bc17704 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -19,9 +19,12 @@ package com.android.keyguard; 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 @@ public class KeyguardPasswordViewController 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 afd42cbfdb25..61f9800c351b 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.DeviceEntryFaceAuthInt 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 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard 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 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardTransitionInteractor.startDismissKeyguardTransition( + mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition( "KeyguardSecurityContainerController#finish"); } } @@ -458,7 +458,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard DeviceProvisionedController deviceProvisionedController, FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate, DevicePolicyManager devicePolicyManager, - KeyguardTransitionInteractor keyguardTransitionInteractor, + KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor, Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor, Provider<DeviceEntryInteractor> deviceEntryInteractor ) { @@ -490,7 +490,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard 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 9b45fa47cf21..f731186f6d01 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3811,7 +3811,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab 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/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index 9b6501eb1e57..2f0ca6e6bf9d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -482,6 +482,10 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } else { // mode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW mEditButton.setVisibility(View.VISIBLE); mAllowDiagonalScrollingView.setVisibility(View.VISIBLE); + if (Flags.saveAndRestoreMagnificationSettingsButtons()) { + selectedButtonIndex = + windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); + } } break; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java index e4b7b7e69c61..275147e6694c 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java @@ -21,11 +21,13 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import android.content.Context; import android.hardware.display.DisplayManager; +import android.os.Handler; import android.os.UserHandle; import android.text.TextUtils; import android.view.Display; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IUserInitializationCompleteCallback; import androidx.annotation.MainThread; @@ -68,6 +70,9 @@ public class AccessibilityFloatingMenuController implements private int mBtnMode; private String mBtnTargets; private boolean mIsKeyguardVisible; + private boolean mIsUserInInitialization; + @VisibleForTesting + Handler mHandler; @VisibleForTesting final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() { @@ -86,18 +91,14 @@ public class AccessibilityFloatingMenuController implements @Override public void onUserSwitching(int userId) { destroyFloatingMenu(); - } - - @Override - public void onUserSwitchComplete(int userId) { - mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); - mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); - mBtnTargets = - mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); - handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets); + mIsUserInInitialization = true; } }; + @VisibleForTesting + final UserInitializationCompleteCallback mUserInitializationCompleteCallback = + new UserInitializationCompleteCallback(); + @Inject public AccessibilityFloatingMenuController(Context context, WindowManager windowManager, @@ -109,7 +110,8 @@ public class AccessibilityFloatingMenuController implements KeyguardUpdateMonitor keyguardUpdateMonitor, SecureSettings secureSettings, DisplayTracker displayTracker, - NavigationModeController navigationModeController) { + NavigationModeController navigationModeController, + Handler handler) { mContext = context; mWindowManager = windowManager; mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager; @@ -121,6 +123,7 @@ public class AccessibilityFloatingMenuController implements mSecureSettings = secureSettings; mDisplayTracker = displayTracker; mNavigationModeController = navigationModeController; + mHandler = handler; mIsKeyguardVisible = false; } @@ -159,6 +162,8 @@ public class AccessibilityFloatingMenuController implements mAccessibilityButtonModeObserver.addListener(this); mAccessibilityButtonTargetsObserver.addListener(this); mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); + mAccessibilityManager.registerUserInitializationCompleteCallback( + mUserInitializationCompleteCallback); } /** @@ -172,7 +177,7 @@ public class AccessibilityFloatingMenuController implements */ private void handleFloatingMenuVisibility(boolean keyguardVisible, @AccessibilityButtonMode int mode, String targets) { - if (keyguardVisible) { + if (keyguardVisible || mIsUserInInitialization) { destroyFloatingMenu(); return; } @@ -210,4 +215,18 @@ public class AccessibilityFloatingMenuController implements mFloatingMenu.hide(); mFloatingMenu = null; } + + class UserInitializationCompleteCallback + extends IUserInitializationCompleteCallback.Stub { + @Override + public void onUserInitializationComplete(int userId) { + mIsUserInInitialization = false; + mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0); + mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode(); + mBtnTargets = + mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets(); + mHandler.post( + () -> handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets)); + } + } } 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 a093f58b88ba..fd06bb174ea3 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 android.view.VelocityTracker 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.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.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 @@ constructor( 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 @@ constructor( @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 @@ constructor( // 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 @@ constructor( 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 05b46564c5ba..e54dc7dbdebb 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 @@ constructor( emptyFlow() } } - .collectLatest { messageViewModel -> message.value = messageViewModel } + .collect { messageViewModel -> message.value = messageViewModel } } private suspend fun listenForSimBouncerEvents() { @@ -170,7 +170,7 @@ constructor( 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 2a272714db37..2d57e5b4f204 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.bouncer.domain.interactor.BouncerInteractor 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 @@ constructor( 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 b985fc4efece..adc4bc9a14f3 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 @@ constructor( launch { userSwitcher.selectedUser .map { it.image.toBitmap() } - .collectLatest { _selectedUserImage.value = it } + .collect { _selectedUserImage.value = it } } launch { @@ -187,34 +187,32 @@ constructor( ) } } - .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 fc860e53aac5..2493cf1a101b 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.delay 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 @@ constructor( combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus -> hasInput && !hasFocus } - .collectLatest { _isTextFieldFocusRequested.value = it } - } - launch { - selectedUserInteractor.selectedUser.collectLatest { _selectedUserId.value = it } + .collect { _isTextFieldFocusRequested.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 @@ constructor( ) { 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 60ec3019d09d..0a866b43429f 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.coroutineScope 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 @@ constructor( 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 db78a98d96af..df6ca9bf0511 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.coroutineScope 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 @@ constructor( } else { interactor.hintedPinLength } - .collectLatest { _hintedPinLength.value = it } + .collect { _hintedPinLength.value = it } } launch { combine( @@ -135,17 +134,17 @@ constructor( 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/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt index 3cdb57318e8d..aef5f1f422d1 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt @@ -38,5 +38,5 @@ sealed class Icon { } /** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */ -fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon = +fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded = Icon.Loaded(this, contentDescription) 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 5077e8d35267..e04d3095d68d 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 @@ constructor( 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/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt index dff391a32d6b..f5914104d87f 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt @@ -60,14 +60,23 @@ interface DeviceEntryFaceAuthInteractor : CoreStartable { fun unregisterListener(listener: FaceAuthenticationListener) fun onUdfpsSensorTouched() + fun onAssistantTriggeredOnLockScreen() + fun onDeviceLifted() - fun onQsExpansionStared() + + fun onQsExpansionStarted() + fun onNotificationPanelClicked() + fun onSwipeUpOnBouncer() + fun onPrimaryBouncerUserInput() + fun onAccessibilityAction() + fun onWalletLaunched() + fun onDeviceUnfolded() /** Whether face auth is considered class 3 */ diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt index de5d0aa5aad7..b7d2a57d9a41 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt @@ -47,6 +47,7 @@ class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceA override fun isFaceAuthEnabledAndEnrolled(): Boolean = false override fun isFaceAuthStrong(): Boolean = false + override fun start() = Unit override fun registerListener(listener: FaceAuthenticationListener) {} @@ -59,13 +60,17 @@ class NoopDeviceEntryFaceAuthInteractor @Inject constructor() : DeviceEntryFaceA override fun onDeviceLifted() {} - override fun onQsExpansionStared() {} + override fun onQsExpansionStarted() {} override fun onNotificationPanelClicked() {} override fun onSwipeUpOnBouncer() {} + override fun onPrimaryBouncerUserInput() {} + override fun onAccessibilityAction() {} + override fun onWalletLaunched() = Unit + override fun onDeviceUnfolded() {} } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt index 183e0e96e765..5ef63d9b856c 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt @@ -60,6 +60,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOn @@ -214,6 +215,16 @@ constructor( } } .launchIn(applicationScope) + + if (SceneContainerFlag.isEnabled) { + sceneInteractor + .get() + .transitionState + .filter { it.isTransitioning(from = Scenes.QuickSettings, to = Scenes.Shade) } + .distinctUntilChanged() + .onEach { onQsExpansionStarted() } + .launchIn(applicationScope) + } } private val isBouncerVisible: Flow<Boolean> by lazy { @@ -239,7 +250,7 @@ constructor( runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true) } - override fun onQsExpansionStared() { + override fun onQsExpansionStarted() { runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true) } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt index 40e2f174cfb7..1f5878b4698f 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt @@ -48,6 +48,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.shareIn @@ -184,6 +185,9 @@ constructor( if (Flags.enableEfficientDisplayRepository()) { enabledDisplayIds .mapElementsLazily { displayId -> getDisplay(displayId) } + .onEach { + if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.") + } .flowOn(backgroundCoroutineDispatcher) .debugLog("enabledDisplays") .stateIn( @@ -194,7 +198,8 @@ constructor( // performance concerns. // Ultimately, this is a trade-off between a one-time UI thread binder call and // the constant overhead of sharedFlows. - initialValue = getDisplays()) + initialValue = getDisplays() + ) } else { oldEnabledDisplays } @@ -380,9 +385,8 @@ constructor( val resultSet: Set<V> ) - val initialState = State(emptySet<T>(), emptyMap(), emptySet<V>()) - - return this.scan(initialState) { state, currentSet -> + val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>()) + return this.scan(emptyInitialState) { state, currentSet -> if (currentSet == state.previousSet) { state } else { @@ -397,6 +401,7 @@ constructor( State(currentSet, newMap, resultSet) } } + .filter { it != emptyInitialState } .map { it.resultSet } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index c3bc24f480dc..e3f740e6ff72 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; - 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mExecutor.execute(() -> { setLifecycleStateLocked(Lifecycle.State.DESTROYED); - - resetCurrentDreamOverlayLocked(); - mDestroyed = true; + mResetHandler.reset("destroying"); }); mDispatcher.onServicePreSuperOnDestroy(); @@ -385,7 +489,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ @Override public void onEndDream() { - resetCurrentDreamOverlayLocked(); + mResetHandler.reset("ending dream"); } @Override @@ -566,46 +673,4 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ 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 cd0b3f9b6693..6318dc000c21 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.interruption.VisualInterrupti 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 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha 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 180afb2b259a..e89594e58aa0 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.RemoteAnimationTarget 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 @@ constructor( private val activityTaskManagerService: IActivityTaskManager, private val keyguardStateController: KeyguardStateController, private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier, - private val keyguardTransitionInteractor: KeyguardTransitionInteractor, + private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, ) { /** @@ -148,7 +148,7 @@ constructor( // 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 1042ae32fea0..e4b0f6ea69a9 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 javax.inject.Inject 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.onEach 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 6e04133dcb4a..4cf9ec8890d4 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 @@ constructor( .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 4666430398ec..2434b29c0cdd 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 @@ constructor( 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 @@ constructor( 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 fc70ea5cd526..228e01edc99b 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 @@ constructor( .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 @@ constructor( 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 f9ab1bbcc741..bde0f56aa691 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 @@ constructor( 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 @@ constructor( ) } } 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 000000000000..c19bbbce3b4b --- /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 4aef8084e5e7..44aafabb103a 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 @@ constructor( @Application scope: CoroutineScope, val repository: KeyguardRepository, val biometricSettingsRepository: BiometricSettingsRepository, - transitionInteractor: KeyguardTransitionInteractor, + keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, internalTransitionInteractor: InternalKeyguardTransitionInteractor, ) { @@ -94,7 +94,9 @@ constructor( 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 4a8ada7f1184..505c749d9e44 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.combine 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 @@ constructor( 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 c10bacf32a5a..d11a41eac3a1 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.dagger.qualifiers.Application 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.MutableSharedFlow 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.mapLatest 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 @@ class KeyguardTransitionInteractor 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 @@ constructor( } /** 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 @@ constructor( it.from } } - .distinctUntilChanged() .stateIn(scope, SharingStarted.Eagerly, OFF) val isInTransition = @@ -425,33 +350,6 @@ constructor( } /** - * 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 @@ constructor( 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 @@ constructor( return finishedKeyguardState.map { it == state }.distinctUntilChanged() } - fun getCurrentState(): KeyguardState { - return currentKeyguardState.replayCache.last() + 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 getStartedFromState(): KeyguardState { - return startedKeyguardFromState.replayCache.last() + fun getCurrentState(): KeyguardState { + return currentKeyguardState.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 f0bf4029ab39..9b8d9ea11edf 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.device 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 @@ constructor( 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 47818cbfd2f2..e00e33df62eb 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.CoroutineDispatcher 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 @@ constructor( 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 906d58664de9..e404f273a768 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.KeyguardState 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 @@ constructor( 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 950eafa23043..ba12e9356ed7 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.CoroutineDispatcher 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 @@ sealed class TransitionInteractor( 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 @@ sealed class TransitionInteractor( " $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 @@ sealed class TransitionInteractor( 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 25b2b7cad7ec..ac874005b612 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 @@ constructor( ) { 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 @@ constructor( 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 @@ constructor( // 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 @@ constructor( // *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 @@ constructor( !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 b8500952d90a..ffd7812166db 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 @@ constructor( } else { val targetState = if (idle.currentScene == Scenes.Lockscreen) { - transitionInteractor.getStartedFromState() + transitionInteractor.startedKeyguardTransitionStep.value.from } else { UNDEFINED } @@ -155,7 +155,7 @@ constructor( 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 24db3c2c70a2..080ddfd18370 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 @@ enum class KeyguardState { 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 0032c2f036d3..e2ad46353c51 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.LockscreenToGoneTransitionView 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 @@ abstract class DeviceEntryIconTransitionModule { @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 6f8389fc8b7c..9f6821051757 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 @@ constructor( alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel, dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel, primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel, + occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel, ) { val color: Flow<Int> = deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBackground -> @@ -103,6 +104,7 @@ constructor( 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 06b76b3c0f37..87c32a54438e 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 @@ constructor( ) { 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 5ce1b5e3dcc5..d3bb4f5d7508 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 @@ constructor( 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 c885c9a5a29b..fe4ebfedee40 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 @@ constructor( 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 a96869df001a..ebdcaa0c91a6 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 @@ constructor( 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 @@ constructor( .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 @@ constructor( // 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 fcafd5e9151d..adb63b7b3e69 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.MutableStateFlow 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 @@ constructor( 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 7383f57bc4e5..2819e617629d 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 com.android.systemui.util.kotlin.filterValuesNotNull 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 @@ constructor( } } } - .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 af019300c764..4fb2b9b35e88 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 @@ import kotlinx.coroutines.flow.Flow class OccludedToDozingTransitionViewModel @Inject constructor( + deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, animationFlow: KeyguardTransitionAnimationFlow, -) { +) : DeviceEntryIconTransition { private val transitionAnimation = animationFlow.setup( duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION, @@ -50,4 +55,17 @@ constructor( 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 e45d537155fd..708b4085da7f 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 @@ constructor( ) { /** 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 f5a7966b7c1e..bf9ef8c5d24e 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.deviceentry.domain.interactor.DeviceEntryInteractor 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.CoroutineDispatcher 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 @@ constructor( 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 @@ constructor( 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/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt index d948dfd7d3b9..c75b601ab4a8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt @@ -16,7 +16,6 @@ package com.android.systemui.qs.panels.ui.compose -import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.runtime.Composable @@ -24,7 +23,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.panels.shared.model.SizedTileImpl @@ -33,7 +31,6 @@ import com.android.systemui.qs.panels.ui.viewmodel.FixedColumnsSizeViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec -import com.android.systemui.res.R import javax.inject.Inject @SysUISingleton @@ -64,7 +61,7 @@ constructor( Tile( tile = sizedTiles[index].tile, iconOnly = iconTilesViewModel.isIconTile(sizedTiles[index].tile.spec), - modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)) + modifier = Modifier ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index a9027ff92996..eeb55ca19bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -16,18 +16,15 @@ package com.android.systemui.qs.panels.ui.compose -import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel -import com.android.systemui.res.R @Composable fun QuickQuickSettings( @@ -54,11 +51,7 @@ fun QuickQuickSettings( key = { index -> sizedTiles[index].tile.spec.spec }, span = { index -> GridItemSpan(sizedTiles[index].width) } ) { index -> - Tile( - tile = tiles[index], - iconOnly = sizedTiles[index].isIcon, - modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)) - ) + Tile(tile = tiles[index], iconOnly = sizedTiles[index].isIcon, modifier = Modifier) } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt index 79c2eb90af20..24af09d62313 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt @@ -44,7 +44,6 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -136,23 +135,23 @@ fun Tile( // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) - val iconShape = TileDefaults.animateIconShape(uiState.state) TileContainer( colors = colors, showLabels = showLabels, label = uiState.label, iconOnly = iconOnly, - shape = if (iconOnly) iconShape else tileShape, + shape = tileShape, clickEnabled = true, onClick = tile::onClick, onLongClick = tile::onLongClick, - modifier = modifier, + modifier = modifier.height(tileHeight()), ) { val icon = getTileIcon(icon = uiState.icon) if (iconOnly) { TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center)) } else { + val iconShape = TileDefaults.animateIconShape(uiState.state) LargeTileContent( label = uiState.label, secondaryLabel = uiState.secondaryLabel, @@ -199,7 +198,7 @@ private fun TileContainer( Expandable( color = backgroundColor, shape = shape, - modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height)).clip(shape) + modifier = Modifier.height(tileHeight()).clip(shape) ) { Box( modifier = @@ -246,7 +245,7 @@ private fun LargeTileContent( // Icon Box( modifier = - Modifier.fillMaxHeight().aspectRatio(1f).thenIf(toggleClickSupported) { + Modifier.size(TileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) { Modifier.clip(iconShape) .background(colors.iconBackground, { 1f }) .combinedClickable(onClick = onClick, onLongClick = onLongClick) @@ -673,7 +672,7 @@ private fun TileIcon( animateToEnd: Boolean = false, modifier: Modifier = Modifier, ) { - val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size)) + val iconModifier = modifier.size(TileDefaults.IconSize) val context = LocalContext.current val loadedDrawable = remember(icon, context) { @@ -710,17 +709,12 @@ private fun TileIcon( } } -@Composable private fun Modifier.tilePadding(): Modifier { - return padding(dimensionResource(id = R.dimen.qs_label_container_margin)) + return padding(TileDefaults.TilePadding) } -@Composable private fun tileHorizontalArrangement(): Arrangement.Horizontal { - return spacedBy( - space = dimensionResource(id = R.dimen.qs_label_container_margin), - alignment = Alignment.Start - ) + return spacedBy(space = TileDefaults.TileArrangementPadding, alignment = Alignment.Start) } @Composable @@ -728,7 +722,7 @@ fun tileHeight(iconWithLabel: Boolean = false): Dp { return if (iconWithLabel) { TileDefaults.IconTileWithLabelHeight } else { - dimensionResource(id = R.dimen.qs_tile_height) + TileDefaults.TileHeight } } @@ -749,6 +743,14 @@ private object TileDefaults { val InactiveCornerRadius = 50.dp val ActiveIconCornerRadius = 16.dp val ActiveTileCornerRadius = 24.dp + + val ToggleTargetSize = 56.dp + val IconSize = 24.dp + + val TilePadding = 8.dp + val TileArrangementPadding = 6.dp + + val TileHeight = 72.dp val IconTileWithLabelHeight = 140.dp /** An active tile without dual target uses the active color as background */ @@ -812,7 +814,7 @@ private object TileDefaults { fun animateIconShape(state: Int): Shape { return animateShape( state = state, - activeCornerRadius = ActiveTileCornerRadius, + activeCornerRadius = ActiveIconCornerRadius, label = "QSTileCornerRadius", ) } @@ -821,7 +823,7 @@ private object TileDefaults { fun animateTileShape(state: Int): Shape { return animateShape( state = state, - activeCornerRadius = ActiveIconCornerRadius, + activeCornerRadius = ActiveTileCornerRadius, label = "QSTileIconCornerRadius", ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java index 71f8639c1e22..89b9eee52f2a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java @@ -306,6 +306,8 @@ public class InternetDialogDelegate implements mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE); mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context)); mWifiRecyclerView.setAdapter(mAdapter); + + updateDialogUI(getWifiNetworkContent()); } @Override @@ -315,6 +317,7 @@ public class InternetDialogDelegate implements } mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED); + mInternetDialogController.onStart(this, mCanConfigWifi); if (!mCanConfigWifi) { hideWifiViews(); @@ -402,10 +405,12 @@ public class InternetDialogDelegate implements internetContent.mShouldUpdateMobileNetwork = shouldUpdateMobileNetwork; internetContent.mInternetDialogTitleString = getDialogTitleText(); internetContent.mInternetDialogSubTitle = getSubtitleText(); - internetContent.mActiveNetworkIsCellular = - mInternetDialogController.activeNetworkIsCellular(); - internetContent.mIsCarrierNetworkActive = - mInternetDialogController.isCarrierNetworkActive(); + if (shouldUpdateMobileNetwork) { + internetContent.mActiveNetworkIsCellular = + mInternetDialogController.activeNetworkIsCellular(); + internetContent.mIsCarrierNetworkActive = + mInternetDialogController.isCarrierNetworkActive(); + } internetContent.mIsAirplaneModeEnabled = mInternetDialogController.isAirplaneModeEnabled(); internetContent.mHasEthernet = mInternetDialogController.hasEthernet(); internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled(); @@ -416,6 +421,15 @@ public class InternetDialogDelegate implements return internetContent; } + private InternetContent getWifiNetworkContent() { + InternetContent internetContent = new InternetContent(); + internetContent.mInternetDialogTitleString = getDialogTitleText(); + internetContent.mInternetDialogSubTitle = getSubtitleText(); + internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled(); + internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked(); + return internetContent; + } + private void setOnClickListener(SystemUIDialog dialog) { mMobileNetworkLayout.setOnClickListener(v -> { int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 3f18fc2066eb..664951d199a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -20,10 +20,12 @@ import android.app.Flags import android.content.Context import android.os.UserHandle import com.android.app.tracing.coroutines.flow.map +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel +import com.android.systemui.res.R import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -52,18 +54,32 @@ constructor( */ fun tileData() = zenModeInteractor.activeModes - .map { modes -> - ModesTileModel( - isActivated = modes.isNotEmpty(), - icon = - if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons()) - zenModeInteractor.getActiveModeIcon(modes) - else null, - activeModes = modes.map { it.name } - ) + .map { activeModes -> + val modesIconResId = R.drawable.qs_dnd_icon_off + + if (usesModeIcons()) { + val mainModeDrawable = activeModes.mainMode?.icon?.drawable + val iconResId = if (mainModeDrawable == null) modesIconResId else null + + ModesTileModel( + isActivated = activeModes.isAnyActive(), + icon = (mainModeDrawable ?: context.getDrawable(modesIconResId)!!).asIcon(), + iconResId = iconResId, + activeModes = activeModes.modeNames + ) + } else { + ModesTileModel( + isActivated = activeModes.isAnyActive(), + icon = context.getDrawable(modesIconResId)!!.asIcon(), + iconResId = modesIconResId, + activeModes = activeModes.modeNames + ) + } } .flowOn(bgDispatcher) .distinctUntilChanged() override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi()) + + private fun usesModeIcons() = Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt index 904ff3aaad26..db4812342050 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt @@ -21,5 +21,12 @@ import com.android.systemui.common.shared.model.Icon data class ModesTileModel( val isActivated: Boolean, val activeModes: List<String>, - val icon: Icon? = null + val icon: Icon.Loaded, + + /** + * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a + * resource with a known id in SystemUI (such as resources from `android.R`, + * `com.android.internal.R`, or `com.android.systemui.res` itself). + */ + val iconResId: Int? = null ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt index 83c3335ebffb..7f571b135fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt @@ -16,11 +16,9 @@ package com.android.systemui.qs.tiles.impl.modes.ui -import android.app.Flags import android.content.res.Resources import android.icu.text.MessageFormat import android.widget.Button -import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel @@ -38,15 +36,10 @@ constructor( ) : QSTileDataToStateMapper<ModesTileModel> { override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState = QSTileState.build(resources, theme, config.uiConfig) { - if (Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons() && data.icon != null) { - icon = { data.icon } - } else { - val iconRes = - if (data.isActivated) R.drawable.qs_dnd_icon_on else R.drawable.qs_dnd_icon_off - val icon = resources.getDrawable(iconRes, theme).asIcon() - this.iconRes = iconRes - this.icon = { icon } + if (!android.app.Flags.modesUiIcons()) { + iconRes = data.iconResId } + icon = { data.icon } activationState = if (data.isActivated) { QSTileState.ActivationState.ACTIVE 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 af55f5a9a968..2bb5dc66bc16 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 com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel 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 @@ constructor( } } } - .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 d2967b87b967..9956a46d4701 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 com.android.systemui.shade.shared.model.ShadeAlignment 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 @@ constructor( } } } - .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 6c63c9762e5e..751448fe607e 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.KeyguardBottomAreaRefactor 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 @@ object SceneContainerFlag { KeyguardBottomAreaRefactor.isEnabled && KeyguardWmStateRefactor.isEnabled && MigrateClocksToBlueprint.isEnabled && - NotificationsHeadsUpRefactor.isEnabled && + NotificationThrottleHun.isEnabled && PredictiveBackSysUiFlag.isEnabled && DeviceEntryUdfpsRefactor.isEnabled @@ -59,7 +59,7 @@ object SceneContainerFlag { 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 b707a5ae4739..88d4c4f63fdf 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.domain.interactor.ShadeInteractor 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 @@ constructor( } } } - .collectLatest { setActions(it) } + .collect { setActions(it) } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java index 9db1f24d2e2e..ad5e772934c8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java @@ -71,7 +71,9 @@ import com.android.systemui.res.R; import com.android.systemui.screenshot.scroll.CropView; import com.android.systemui.settings.UserTracker; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -344,10 +346,63 @@ public class AppClipsActivity extends ComponentActivity { // Set up the dropdown when multiple backlinks are available. if (backlinksData.size() > 1) { - setUpListPopupWindow(backlinksData, mBacklinksDataTextView); + setUpListPopupWindow(updateBacklinkLabelsWithDuplicateNames(backlinksData), + mBacklinksDataTextView); } } + /** + * If there are more than 1 backlinks that have the same app name, then this method appends + * a numerical suffix to such backlinks to help users distinguish. + */ + private List<InternalBacklinksData> updateBacklinkLabelsWithDuplicateNames( + List<InternalBacklinksData> backlinksData) { + // Check if there are multiple backlinks with same name. + Map<String, Integer> duplicateNamedBacklinksCountMap = new HashMap<>(); + for (InternalBacklinksData data : backlinksData) { + if (duplicateNamedBacklinksCountMap.containsKey(data.getDisplayLabel())) { + int duplicateCount = duplicateNamedBacklinksCountMap.get(data.getDisplayLabel()); + if (duplicateCount == 0) { + // If this is the first time the loop is coming across a duplicate name, set the + // count to 2. This way the count starts from 1 for all duplicate named + // backlinks. + duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 2); + } else { + // For all duplicate named backlinks, increase the duplicate count by 1. + duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), duplicateCount + 1); + } + } else { + // This is the first time the loop is coming across a backlink with this name. Set + // its count to 0. The loop will increase its count by 1 when a duplicate is found. + duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 0); + } + } + + // Go through the backlinks in reverse order as it is easier to assign the numerical suffix + // in descending order of frequency using the duplicate map that was built earlier. For + // example, if "App A" is present 3 times, then we assign display label "App A (3)" first + // and then "App A (2)", lastly "App A (1)". + for (InternalBacklinksData data : backlinksData.reversed()) { + String originalBacklinkLabel = data.getDisplayLabel(); + int duplicateCount = duplicateNamedBacklinksCountMap.get(originalBacklinkLabel); + + // The display label should only be updated if there are multiple backlinks with the + // same name. + if (duplicateCount > 0) { + // Update the display label to: "App name (count)" + data.setDisplayLabel( + getString(R.string.backlinks_duplicate_label_format, originalBacklinkLabel, + duplicateCount)); + + // Decrease the duplicate count and update the map. + duplicateCount--; + duplicateNamedBacklinksCountMap.put(originalBacklinkLabel, duplicateCount); + } + } + + return backlinksData; + } + private void setUpListPopupWindow(List<InternalBacklinksData> backlinksData, View anchor) { ListPopupWindow listPopupWindow = new ListPopupWindow(this); listPopupWindow.setAnchorView(anchor); @@ -365,7 +420,7 @@ public class AppClipsActivity extends ComponentActivity { public View getView(int position, @Nullable View convertView, ViewGroup parent) { TextView itemView = (TextView) super.getView(position, convertView, parent); InternalBacklinksData data = backlinksData.get(position); - itemView.setText(data.getClipData().getDescription().getLabel()); + itemView.setText(data.getDisplayLabel()); Drawable icon = data.getAppIcon(); icon.setBounds(createBacklinksTextViewDrawableBounds()); @@ -387,7 +442,7 @@ public class AppClipsActivity extends ComponentActivity { * expected to be already set when this method is called. */ private void updateBacklinksTextView(InternalBacklinksData backlinksData) { - mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel()); + mBacklinksDataTextView.setText(backlinksData.getDisplayLabel()); Drawable appIcon = backlinksData.getAppIcon(); Rect compoundDrawableBounds = createBacklinksTextViewDrawableBounds(); appIcon.setBounds(compoundDrawableBounds); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt index 0e312f979d0c..30c33c5224ee 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt @@ -20,4 +20,6 @@ import android.content.ClipData import android.graphics.drawable.Drawable /** A class to hold the [ClipData] for backlinks and the corresponding app's [Drawable] icon. */ -internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable) +internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable) { + var displayLabel: String = clipData.description.label.toString() +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 4639e2235346..3bb494b7deca 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 @@ constructor( */ 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 @@ constructor( ), { (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 @@ constructor( 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 c023b8358a01..31813b240c37 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.PropertyAnimator; 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ActivityStarter activityStarter, SharedNotificationContainerInteractor sharedNotificationContainerInteractor, ActiveNotificationsInteractor activeNotificationsInteractor, - HeadsUpNotificationInteractor headsUpNotificationInteractor, ShadeAnimationInteractor shadeAnimationInteractor, KeyguardViewConfigurator keyguardViewConfigurator, DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, @@ -811,7 +806,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardTransitionInteractor = keyguardTransitionInteractor; mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor; mActiveNotificationsInteractor = activeNotificationsInteractor; - mHeadsUpNotificationInteractor = headsUpNotificationInteractor; mKeyguardInteractor = keyguardInteractor; mPowerInteractor = powerInteractor; mKeyguardViewConfigurator = keyguardViewConfigurator; @@ -1222,11 +1216,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } }, mMainDispatcher); - - if (NotificationsHeadsUpRefactor.isEnabled()) { - collectFlow(mView, mHeadsUpNotificationInteractor.isHeadsUpOrAnimatingAway(), - setHeadsUpVisible(), mMainDispatcher); - } } @VisibleForTesting @@ -3077,21 +3066,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener { @Override public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); - if (inPinnedMode) { mHeadsUpExistenceChangedRunnable.run(); updateNotificationTranslucency(); @@ -4464,8 +4432,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void onHeadsUpPinned(NotificationEntry entry) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); - if (!isKeyguardShowing()) { mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true); } @@ -4473,8 +4439,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @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/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index 16aef6586ee9..34c0cb7c7a31 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -1005,7 +1005,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum // When expanding QS, let's authenticate the user if possible, // this will speed up notification actions. if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) { - mDeviceEntryFaceAuthInteractor.onQsExpansionStared(); + mDeviceEntryFaceAuthInteractor.onQsExpansionStarted(); } } 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 f270e821840a..9c4bf1faf5cd 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.scene.shared.model.Scenes 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 @@ constructor( 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 c210fc509cec..abf1f4cb8b85 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.awaitCancellation 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 @@ constructor( 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 c8abb1fe1dd5..a154e91feca1 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.coroutineScope 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 @@ constructor( 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 bdc0fdba1dea..ab719132b93e 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.domain.interactor.ShadeInteractor 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 @@ constructor( } } } - .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 7a79a22ebea2..7c707592f5ab 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.AssistedFactory 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 @@ constructor( 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/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 6eadd2627399..2b44c2f9ea7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -58,6 +58,7 @@ import androidx.core.graphics.ColorUtils; import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIcon.Shape; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Flags; import com.android.systemui.res.R; @@ -211,16 +212,19 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi /** Should always be preceded by {@link #reloadDimens()} */ @VisibleForTesting public void maybeUpdateIconScaleDimens() { - // We do not resize and scale system icons (on the right), only notification icons (on the - // left). - if (isNotification()) { - updateIconScaleForNotifications(); + // We scale notification icons (on the left) plus icons on the right that explicitly + // want FIXED_SPACE. + boolean useNonSystemIconScaling = isNotification() + || (usesModeIcons() && mIcon != null && mIcon.shape == Shape.FIXED_SPACE); + + if (useNonSystemIconScaling) { + updateIconScaleForNonSystemIcons(); } else { updateIconScaleForSystemIcons(); } } - private void updateIconScaleForNotifications() { + private void updateIconScaleForNonSystemIcons() { float iconScale; // we need to scale the image size to be same as the original size // (fit mOriginalStatusBarIconSize), then we can scale it with mScaleToFitNewIconSize @@ -411,7 +415,9 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi if (!levelEquals) { setImageLevel(icon.iconLevel); } - + if (usesModeIcons()) { + setScaleType(icon.shape == Shape.FIXED_SPACE ? ScaleType.FIT_CENTER : ScaleType.CENTER); + } if (!visibilityEquals) { setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE); } @@ -501,7 +507,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Nullable private Drawable loadDrawable(Context context, StatusBarIcon statusBarIcon) { if (usesModeIcons() && statusBarIcon.preloadedIcon != null) { - return statusBarIcon.preloadedIcon.mutate(); + Drawable.ConstantState cached = statusBarIcon.preloadedIcon.getConstantState(); + if (cached != null) { + return cached.newDrawable(mContext.getResources()).mutate(); + } else { + return statusBarIcon.preloadedIcon.mutate(); + } } else { int userId = statusBarIcon.user.getIdentifier(); if (userId == UserHandle.USER_ALL) { 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 173ff37cc3b8..be733d4e1e8e 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 18ea0b445481..e82525810c64 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 @@ constructor( 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 cf4e7072a7d1..d4ad6ee0d04b 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 @@ constructor( ): 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 @@ constructor( 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 000000000000..84ccaec62867 --- /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/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt new file mode 100644 index 000000000000..4ef190991f19 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt @@ -0,0 +1,61 @@ +/* + * 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.shared + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the status bar RON chips flag state. */ +@Suppress("NOTHING_TO_INLINE") +object StatusBarRonChips { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.statusBarRonChips() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * 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 + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} 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 9e6cacb8b9ff..eb735211a970 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 @@ constructor( 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 7897f93b6496..d99a916b78a7 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 @@ constructor( ): 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 26a2f9139608..62622a893ec5 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 @@ sealed class OngoingActivityChipModel { ) : 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 @@ sealed class OngoingActivityChipModel { } } - /** 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 b0d897def53f..04c4516c9bed 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.log.core.LogLevel 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 @@ constructor( shareToAppChipViewModel: ShareToAppChipViewModel, castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, + demoRonChipViewModel: DemoRonChipViewModel, @StatusBarChipsLog private val logger: LogBuffer, ) { private enum class ChipType { @@ -58,6 +61,8 @@ constructor( 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 @@ constructor( val shareToApp: OngoingActivityChipModel.Hidden, val castToOtherDevice: OngoingActivityChipModel.Hidden, val call: OngoingActivityChipModel.Hidden, + val demoRon: OngoingActivityChipModel.Hidden, ) : InternalChipModel } @@ -87,7 +93,8 @@ constructor( 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 @@ constructor( }, { "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 @@ constructor( 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 @@ constructor( 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 @@ constructor( 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 ecb6d7fd7be4..406a66449f82 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.LogBuffer 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 @@ abstract class StatusBarModule { @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 74ec7edd5398..aa203d7c0aa9 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 @@ package com.android.systemui.statusbar.notification.domain.interactor 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 @@ constructor( /** 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 @@ constructor( /** 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 @@ constructor( } val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { combine(hasPinnedRows, headsUpRepository.isHeadsUpAnimatingAway) { @@ -123,7 +123,7 @@ constructor( } 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 163b332a34bd..64d71240073f 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.ExpandableView; 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; @@ -4344,7 +4343,7 @@ public class NotificationStackScrollLayout // 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 { @@ -4455,7 +4454,7 @@ public class NotificationStackScrollLayout void onChildAnimationFinished() { setAnimationRunning(false); - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { setHeadsUpAnimatingAway(false); } requestChildrenUpdate(); @@ -5001,7 +5000,7 @@ public class NotificationStackScrollLayout } public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) { - NotificationsHeadsUpRefactor.assertInLegacyMode(); + SceneContainerFlag.assertInLegacyMode(); ExpandableNotificationRow row = entry.getHeadsUpAnimationView(); generateHeadsUpAnimation(row, isHeadsUp); } @@ -5044,7 +5043,7 @@ public class NotificationStackScrollLayout mNeedsAnimation = true; if (!mIsExpanded && !mWillExpand && !isHeadsUp) { row.setHeadsUpAnimatingAway(true); - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { setHeadsUpAnimatingAway(true); } } @@ -5238,7 +5237,7 @@ public class NotificationStackScrollLayout 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 e9089050cfad..e112c99e19b3 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.NotificationGuts; 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 @@ public class NotificationStackScrollLayoutController implements Dumpable { 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 @@ public class NotificationStackScrollLayoutController implements Dumpable { }); } - if (!NotificationsHeadsUpRefactor.isEnabled()) { + if (!SceneContainerFlag.isEnabled()) { mHeadsUpManager.addListener(mOnHeadsUpChangedListener); } mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); @@ -1509,7 +1508,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { } 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 5572f8e5cbe8..d770b2003f3b 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.dagger.qualifiers.Background 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.view.FooterView 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 @@ constructor( 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 5fba615e020b..e55492e67d02 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 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel 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.domain.interactor.SeenNotific 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 @@ constructor( } val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(null) } else { headsUpNotificationInteractor.topHeadsUpRow.dumpWhileCollecting("topHeadsUpRow") @@ -264,7 +264,7 @@ constructor( } val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(emptySet()) } else { headsUpNotificationInteractor.pinnedHeadsUpRows.dumpWhileCollecting("pinnedHeadsUpRows") @@ -272,7 +272,7 @@ constructor( } val headsUpAnimationsEnabled: Flow<Boolean> by lazy { - if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { flowOf(false) } else { flowOf(true).dumpWhileCollecting("headsUpAnimationsEnabled") @@ -280,7 +280,7 @@ constructor( } 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 e3242d13077f..7227b9380b61 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.keyboardShortcutHelperRewrite; 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 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { 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 0ea28a75094c..1efad3b9fa66 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.HeadsUpReposi 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 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements 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 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements @Nullable private HeadsUpEntryPhone getTopHeadsUpEntryPhone() { - if (NotificationsHeadsUpRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { return (HeadsUpEntryPhone) mTopHeadsUpRow.getValue(); } else { return (HeadsUpEntryPhone) getTopHeadsUpEntry(); @@ -711,7 +710,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements } 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/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 05bd1a7676ae..ba39c3bb4124 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -44,7 +44,7 @@ import android.view.View; import androidx.lifecycle.Observer; -import com.android.settingslib.notification.modes.ZenMode; +import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -80,6 +80,7 @@ import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor; +import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.DateFormatUtil; @@ -363,7 +364,7 @@ public class PhoneStatusBarPolicy // Note that we're not fully replacing ZenModeController with ZenModeInteractor, so // we listen for the extra event here but still add the ZMC callback. mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(), - this::onActiveModeChanged); + this::onMainActiveModeChanged); } mZenController.addCallback(mZenControllerCallback); if (!Flags.statusBarScreenSharingChips()) { @@ -395,20 +396,23 @@ public class PhoneStatusBarPolicy () -> mResources.getString(R.string.accessibility_managed_profile)); } - private void onActiveModeChanged(@Nullable ZenMode mode) { + private void onMainActiveModeChanged(@Nullable ZenModeInfo mainActiveMode) { if (!usesModeIcons()) { - Log.wtf(TAG, "onActiveModeChanged shouldn't be called if MODES_UI_ICONS is disabled"); + Log.wtf(TAG, "onMainActiveModeChanged shouldn't run if MODES_UI_ICONS is disabled"); return; } - boolean visible = mode != null; - if (visible) { - // TODO: b/360399800 - Get the resource id, package, and cached drawable from the mode; - // this is a shortcut for testing. - String resPackage = mode.getIconKey().resPackage(); - int iconResId = mode.getIconKey().resId(); - mIconController.setResourceIcon(mSlotZen, resPackage, iconResId, - /* preloadedIcon= */ null, mode.getName()); + boolean visible = mainActiveMode != null; + if (visible) { + // Shape=FIXED_SPACE because mode icons can be from 3P packages and may not be square; + // we don't want to allow apps to set incredibly wide icons and take up too much space + // in the status bar. + mIconController.setResourceIcon(mSlotZen, + mainActiveMode.getIcon().key().resPackage(), + mainActiveMode.getIcon().key().resId(), + mainActiveMode.getIcon().drawable(), + mainActiveMode.getName(), + StatusBarIcon.Shape.FIXED_SPACE); } if (visible != mZenVisible) { mIconController.setIconVisibility(mSlotZen, visible); 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 da5877b6417f..8f2d4f931b91 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 @@ package com.android.systemui.statusbar.phone; 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 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, } 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 09b6b6895ebc..43f9af6016f1 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.domain.interactor.DeviceEntryInteractor; 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } }; - private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor; - private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor; private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor; private final JavaAdapter mJavaAdapter; private StatusBarKeyguardViewManagerInteractor mStatusBarKeyguardViewManagerInteractor; @@ -394,11 +390,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } KeyguardTransitionInteractor mKeyguardTransitionInteractor; + KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor; CoroutineDispatcher mMainDispatcher; @Override @@ -1616,7 +1611,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } 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 ba5939835a08..d5fafe222c40 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.ArraySet; 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 java.util.Objects; 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 @@ public class StatusBarSignalPolicy implements SignalCallback, 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; - - // Track as little state as possible, and only for padding purposes - private boolean mIsAirplaneMode = false; + private final boolean mActivityEnabled; - private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); + private final ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); private boolean mInitialized; @Inject @@ -80,15 +87,19 @@ public class StatusBarSignalPolicy implements SignalCallback, 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 @@ public class StatusBarSignalPolicy implements SignalCallback, 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 @@ public class StatusBarSignalPolicy implements SignalCallback, @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 class StatusBarSignalPolicy implements SignalCallback, } } + 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 index 62641fe2f229..0577f495dd4f 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 @@ object NotificationsHeadsUpRefactor { /** 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/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java index 8871dae3c620..6c303303c8f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.ui; -import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; @@ -64,9 +63,8 @@ public class DarkIconManager extends IconManager { } @Override - protected LinearLayout.LayoutParams onCreateLayoutParams() { - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); + protected LinearLayout.LayoutParams onCreateLayoutParams(StatusBarIcon.Shape shape) { + LinearLayout.LayoutParams lp = super.onCreateLayoutParams(shape); lp.setMargins(mIconHorizontalMargin, 0, mIconHorizontalMargin, 0); return lp; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java index 5ad737684ca1..91ead614ffa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java @@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_BIND import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; +import static com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl.usesModeIcons; import android.annotation.Nullable; import android.content.Context; @@ -27,9 +28,8 @@ import android.os.Bundle; import android.view.ViewGroup; import android.widget.LinearLayout; -import androidx.annotation.VisibleForTesting; - import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIcon.Shape; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; @@ -155,12 +155,11 @@ public class IconManager implements DemoModeCommandReceiver { }; } - @VisibleForTesting protected StatusBarIconView addIcon(int index, String slot, boolean blocked, StatusBarIcon icon) { StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); view.set(icon); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(icon.shape)); return view; } @@ -174,7 +173,7 @@ public class IconManager implements DemoModeCommandReceiver { int index) { mBindableIcons.put(holder.getSlot(), holder); ModernStatusBarView view = holder.getInitializer().createAndBind(mContext); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT)); if (mIsInDemoMode) { mDemoStatusIcons.addBindableIcon(holder); } @@ -183,7 +182,7 @@ public class IconManager implements DemoModeCommandReceiver { protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT)); if (mIsInDemoMode) { mDemoStatusIcons.addModernWifiView(mWifiViewModel); @@ -199,7 +198,7 @@ public class IconManager implements DemoModeCommandReceiver { int subId ) { BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId); - mGroup.addView(view, index, onCreateLayoutParams()); + mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT)); if (mIsInDemoMode) { Context mobileContext = mMobileContextProvider @@ -233,8 +232,12 @@ public class IconManager implements DemoModeCommandReceiver { ); } - protected LinearLayout.LayoutParams onCreateLayoutParams() { - return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); + protected LinearLayout.LayoutParams onCreateLayoutParams(Shape shape) { + int width = usesModeIcons() && shape == StatusBarIcon.Shape.FIXED_SPACE + ? mIconSize + : ViewGroup.LayoutParams.WRAP_CONTENT; + + return new LinearLayout.LayoutParams(width, mIconSize); } protected void destroy() { @@ -256,6 +259,13 @@ public class IconManager implements DemoModeCommandReceiver { /** Called once an icon has been set. */ public void onSetIcon(int viewIndex, StatusBarIcon icon) { StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex); + if (usesModeIcons()) { + ViewGroup.LayoutParams current = view.getLayoutParams(); + ViewGroup.LayoutParams desired = onCreateLayoutParams(icon.shape); + if (desired.width != current.width || desired.height != current.height) { + view.setLayoutParams(desired); + } + } view.set(icon); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java index ee528e915079..0459b9749e0a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java @@ -70,7 +70,8 @@ public interface StatusBarIconController { * @param preloadedIcon optional drawable corresponding to {@code iconResId}, if known */ void setResourceIcon(String slot, @Nullable String resPackage, @DrawableRes int iconResId, - @Nullable Drawable preloadedIcon, CharSequence contentDescription); + @Nullable Drawable preloadedIcon, CharSequence contentDescription, + StatusBarIcon.Shape shape); /** * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java index ad3a9e350c4b..9b6d32bd179d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java @@ -234,13 +234,14 @@ public class StatusBarIconControllerImpl implements Tunable, Icon.createWithResource(mContext, resourceId), /* preloadedIcon= */ null, contentDescription, - StatusBarIcon.Type.SystemIcon); + StatusBarIcon.Type.SystemIcon, + StatusBarIcon.Shape.WRAP_CONTENT); } @Override public void setResourceIcon(String slot, @Nullable String resPackage, @DrawableRes int iconResId, @Nullable Drawable preloadedIcon, - CharSequence contentDescription) { + CharSequence contentDescription, StatusBarIcon.Shape shape) { if (!usesModeIcons()) { Log.wtf("TAG", "StatusBarIconController.setResourceIcon() should not be called without " @@ -260,12 +261,13 @@ public class StatusBarIconControllerImpl implements Tunable, icon, preloadedIcon, contentDescription, - StatusBarIcon.Type.ResourceIcon); + StatusBarIcon.Type.ResourceIcon, + shape); } private void setResourceIconInternal(String slot, Icon resourceIcon, @Nullable Drawable preloadedIcon, CharSequence contentDescription, - StatusBarIcon.Type type) { + StatusBarIcon.Type type, StatusBarIcon.Shape shape) { checkArgument(resourceIcon.getType() == Icon.TYPE_RESOURCE, "Expected Icon of TYPE_RESOURCE, but got " + resourceIcon.getType()); String resPackage = resourceIcon.getResPackage(); @@ -277,7 +279,7 @@ public class StatusBarIconControllerImpl implements Tunable, if (holder == null) { StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, resPackage, resourceIcon, /* iconLevel= */ 0, /* number=*/ 0, - contentDescription, type); + contentDescription, type, shape); icon.preloadedIcon = preloadedIcon; holder = StatusBarIconHolder.fromIcon(icon); setIcon(slot, holder); @@ -286,6 +288,7 @@ public class StatusBarIconControllerImpl implements Tunable, holder.getIcon().icon = resourceIcon; holder.getIcon().contentDescription = contentDescription; holder.getIcon().type = type; + holder.getIcon().shape = shape; holder.getIcon().preloadedIcon = preloadedIcon; handleSet(slot, holder); } @@ -578,7 +581,7 @@ public class StatusBarIconControllerImpl implements Tunable, } } - private static boolean usesModeIcons() { + static boolean usesModeIcons() { return android.app.Flags.modesApi() && android.app.Flags.modesUi() && android.app.Flags.modesUiIcons(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt index 5ad8bf1652b6..32e9c85bea81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt @@ -21,7 +21,6 @@ import android.telephony.SubscriptionManager import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.settingslib.mobile.MobileMappings import com.android.settingslib.mobile.MobileMappings.Config -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -93,17 +92,15 @@ interface MobileConnectionsRepository { val defaultMobileIconGroup: Flow<MobileIconGroup> /** - * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a - * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]). + * Can the device make emergency calls using the device-based service state? This field is only + * useful when all known active subscriptions are OOS and not emergency call capable. * - * While each [MobileConnectionsRepository] listens for the service state of each subscription, - * there is potentially a service state associated with the device itself. This value can be - * used to calculate e.g., the emergency calling capability of the device (as opposed to the - * emergency calling capability of an individual mobile connection) + * Specifically, this checks every [ServiceState] of the device, and looks for any that report + * [ServiceState.isEmergencyOnly]. * - * Note: this is a [StateFlow] using an eager sharing strategy. + * This is an eager flow, and re-evaluates whenever ACTION_SERVICE_STATE is sent for subId = -1. */ - val deviceServiceState: StateFlow<ServiceStateModel?> + val isDeviceEmergencyCallCapable: StateFlow<Boolean> /** * If any active SIM on the device is in diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt index b0681525a137..b247da4f4219 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt @@ -25,7 +25,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.demomode.DemoMode import com.android.systemui.demomode.DemoModeController -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl @@ -152,16 +151,17 @@ constructor( override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> = activeRepo.flatMapLatest { it.defaultMobileIconGroup } - override val deviceServiceState: StateFlow<ServiceStateModel?> = + override val isDeviceEmergencyCallCapable: StateFlow<Boolean> = activeRepo - .flatMapLatest { it.deviceServiceState } + .flatMapLatest { it.isDeviceEmergencyCallCapable } .stateIn( scope, SharingStarted.WhileSubscribed(), - realRepository.deviceServiceState.value + realRepository.isDeviceEmergencyCallCapable.value ) override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure } + override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure() override val defaultDataSubId: StateFlow<Int> = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt index a944e9133a31..3a79f3fb3573 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt @@ -27,7 +27,6 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository @@ -137,10 +136,11 @@ constructor( override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G) - // TODO(b/339023069): demo command for device-based connectivity state - override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null) + // TODO(b/339023069): demo command for device-based emergency calls state + override val isDeviceEmergencyCallCapable: StateFlow<Boolean> = MutableStateFlow(false) override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure()) + override fun getIsAnySimSecure(): Boolean = false override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index 261258a58914..b756a05da182 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -21,7 +21,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.telephony.CarrierConfigManager -import android.telephony.ServiceState import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID @@ -49,7 +48,6 @@ import com.android.systemui.statusbar.pipeline.airplane.data.repository.Airplane import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy @@ -72,7 +70,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest @@ -175,8 +172,8 @@ constructor( } .flowOn(bgDispatcher) - /** Note that this flow is eager, so we don't miss any state */ - override val deviceServiceState: StateFlow<ServiceStateModel?> = + /** Turn ACTION_SERVICE_STATE (for subId = -1) into an event */ + private val serviceStateChangedEvent: Flow<Unit> = broadcastDispatcher .broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ -> val subId = @@ -185,24 +182,34 @@ constructor( INVALID_SUBSCRIPTION_ID ) - val extras = intent.extras - if (extras == null) { - logger.logTopLevelServiceStateBroadcastMissingExtras(subId) - return@broadcastFlow null - } - - val serviceState = ServiceState.newFromBundle(extras) - logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState) + // Only emit if the subId is not associated with an active subscription if (subId == INVALID_SUBSCRIPTION_ID) { - // Assume that -1 here is the device's service state. We don't care about - // other ones. - ServiceStateModel.fromServiceState(serviceState) - } else { - null + Unit } } - .filterNotNull() - .stateIn(scope, SharingStarted.Eagerly, null) + // Emit on start so that we always check the state at least once + .onStart { emit(Unit) } + + /** Eager flow to determine the device-based emergency calls only state */ + override val isDeviceEmergencyCallCapable: StateFlow<Boolean> = + serviceStateChangedEvent + .mapLatest { + val modems = telephonyManager.activeModemCount + // Check the service state for every modem. If any state reports emergency calling + // capable, then consider the device to have emergency call capabilities + (0..<modems) + .map { telephonyManager.getServiceStateForSlot(it) } + .any { it?.isEmergencyOnly == true } + } + .flowOn(bgDispatcher) + .distinctUntilChanged() + .logDiffsForTable( + tableLogger, + columnPrefix = LOGGING_PREFIX, + columnName = "deviceEmergencyOnly", + initialValue = false, + ) + .stateIn(scope, SharingStarted.Eagerly, false) /** * State flow that emits the set of mobile data subscriptions, each represented by its own diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt index 26553e66ac5c..28fff4e68935 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt @@ -385,15 +385,7 @@ constructor( .stateIn(scope, SharingStarted.WhileSubscribed(), false) override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> = - mobileConnectionsRepo.deviceServiceState - .map { it?.isEmergencyOnly ?: false } - .distinctUntilChanged() - .logDiffsForTable( - tableLogger, - columnPrefix = LOGGING_PREFIX, - columnName = "deviceEmergencyOnly", - initialValue = false, - ) + mobileConnectionsRepo.isDeviceEmergencyCallCapable /** Vends out new [MobileIconInteractor] for a particular subId */ override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor = 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 1a55f7d90e97..f5cfc8c5b307 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 android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID 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.DefaultConnecti 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 @@ constructor( } // 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 d46aaf45b1a3..c24d69465043 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.lifecycle.repeatWhenAttached 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa // 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa // 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa // 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } 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 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa 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/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 71bcdfcba049..6cebcbd2731a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -22,8 +22,10 @@ import android.os.UserManager; import com.android.internal.R; import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; +import com.android.settingslib.notification.modes.ZenIconLoader; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogBufferFactory; import com.android.systemui.settings.UserTracker; @@ -79,6 +81,7 @@ import dagger.Module; import dagger.Provides; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import javax.inject.Named; @@ -236,4 +239,12 @@ public interface StatusBarPolicyModule { static LogBuffer provideCastControllerLog(LogBufferFactory factory) { return factory.create("CastControllerLog", 50); } + + /** Provides a {@link ZenIconLoader} that fetches icons in a background thread. */ + @Provides + @SysUISingleton + static ZenIconLoader provideZenIconLoader( + @UiBackground ExecutorService backgroundExecutorService) { + return new ZenIconLoader(backgroundExecutorService); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt index a67b47a9a0c9..93c631f65df7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt @@ -23,16 +23,20 @@ import android.provider.Settings.Secure.ZEN_DURATION_PROMPT import android.util.Log import androidx.concurrent.futures.await import com.android.settingslib.notification.data.repository.ZenModeRepository +import com.android.settingslib.notification.modes.ZenIcon import com.android.settingslib.notification.modes.ZenIconLoader import com.android.settingslib.notification.modes.ZenMode -import com.android.systemui.common.shared.model.Icon -import com.android.systemui.common.shared.model.asIcon +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository +import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes +import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo import java.time.Duration import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** @@ -45,9 +49,9 @@ constructor( private val context: Context, private val zenModeRepository: ZenModeRepository, private val notificationSettingsRepository: NotificationSettingsRepository, + @Background private val bgDispatcher: CoroutineDispatcher, + private val iconLoader: ZenIconLoader, ) { - private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance() - val isZenModeEnabled: Flow<Boolean> = zenModeRepository.globalZenMode .map { @@ -76,34 +80,27 @@ constructor( val modes: Flow<List<ZenMode>> = zenModeRepository.modes - val activeModes: Flow<List<ZenMode>> = - modes.map { modes -> modes.filter { mode -> mode.isActive } }.distinctUntilChanged() - - /** Flow returning the most prioritized of the active modes, if any. */ - val mainActiveMode: Flow<ZenMode?> = - activeModes.map { modes -> getMainActiveMode(modes) }.distinctUntilChanged() - - /** - * Given the list of modes (which may include zero or more currently active modes), returns the - * most prioritized of the active modes, if any. - */ - private fun getMainActiveMode(modes: List<ZenMode>): ZenMode? { - return modes.sortedWith(ZenMode.PRIORITIZING_COMPARATOR).firstOrNull { it.isActive } - } + /** Flow returning the currently active mode(s), if any. */ + val activeModes: Flow<ActiveZenModes> = + modes + .map { modes -> + val activeModesList = + modes + .filter { mode -> mode.isActive } + .sortedWith(ZenMode.PRIORITIZING_COMPARATOR) + val mainActiveMode = + activeModesList.firstOrNull()?.let { ZenModeInfo(it.name, getModeIcon(it)) } + + ActiveZenModes(activeModesList.map { m -> m.name }, mainActiveMode) + } + .flowOn(bgDispatcher) + .distinctUntilChanged() - suspend fun getModeIcon(mode: ZenMode): Icon { - return iconLoader.getIcon(context, mode).await().drawable().asIcon() - } + val mainActiveMode: Flow<ZenModeInfo?> = + activeModes.map { a -> a.mainMode }.distinctUntilChanged() - /** - * Given the list of modes (which may include zero or more currently active modes), returns an - * icon representing the active mode, if any (or, if multiple modes are active, to the most - * prioritized one). This icon is suitable for use in the status bar or lockscreen (uses the - * standard DND icon for implicit modes, instead of the launcher icon of the associated - * package). - */ - suspend fun getActiveModeIcon(modes: List<ZenMode>): Icon? { - return getMainActiveMode(modes)?.let { m -> getModeIcon(m) } + suspend fun getModeIcon(mode: ZenMode): ZenIcon { + return iconLoader.getIcon(context, mode).await() } fun activateMode(zenMode: ZenMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt new file mode 100644 index 000000000000..569e517d6ed5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.domain.model + +import com.android.settingslib.notification.modes.ZenMode + +/** + * Represents the list of [ZenMode] instances that are currently active. + * + * @property modeNames Names of all the active modes, sorted by their priority. + * @property mainMode The most prioritized active mode, if any modes active. Guaranteed to be + * non-null if [modeNames] is not empty. + */ +data class ActiveZenModes(val modeNames: List<String>, val mainMode: ZenModeInfo?) { + fun isAnyActive(): Boolean = modeNames.isNotEmpty() +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt new file mode 100644 index 000000000000..5004f4c21371 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.domain.model + +import com.android.settingslib.notification.modes.ZenIcon +import com.android.settingslib.notification.modes.ZenMode + +/** Name and icon of a [ZenMode] */ +data class ZenModeInfo(val name: String, val icon: ZenIcon) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt index be90bec03e52..841071347c08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt @@ -23,6 +23,7 @@ import android.provider.Settings.ACTION_AUTOMATIC_ZEN_RULE_SETTINGS import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID import com.android.settingslib.notification.modes.EnableZenModeDialog import com.android.settingslib.notification.modes.ZenMode +import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger @@ -88,7 +89,7 @@ constructor( modesList.map { mode -> ModeTileViewModel( id = mode.id, - icon = zenModeInteractor.getModeIcon(mode), + icon = zenModeInteractor.getModeIcon(mode).drawable().asIcon(), text = mode.name, subtext = getTileSubtext(mode), enabled = mode.isActive, 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 89227cfb2b23..fe1d64736991 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.SysUISingleton 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 @@ constructor( ) { 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 c7fc44513473..9c8ef0421888 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.android.systemui.util.settings.SecureSettings; 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 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { 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 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { } 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 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { 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/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java index ecf1165566dc..70774f13fe6b 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java @@ -28,6 +28,7 @@ import dagger.Module; import dagger.Provides; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.inject.Singleton; @@ -81,6 +82,18 @@ public abstract class GlobalConcurrencyModule { @Singleton @UiBackground public static Executor provideUiBackgroundExecutor() { + return provideUiBackgroundExecutorService(); + } + + /** + * Provide an ExecutorService specifically for running UI operations on a separate thread. + * + * <p>Keep submitted runnables short and to the point, just as with any other UI code. + */ + @Provides + @Singleton + @UiBackground + public static ExecutorService provideUiBackgroundExecutorService() { return Executors.newSingleThreadExecutor(); } 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 055671cf32ca..64e056da97d4 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.CoroutineScope 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 @@ constructor( ): 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 d3e8bd3d8b5b..28effe909521 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 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa 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 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa 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 eb9151868b79..7786453814e0 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 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, 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 0451ce6d9fce..4be680ef66f1 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 @@ class AudioSlidersInteractor constructor( @VolumePanelScope scope: CoroutineScope, mediaOutputInteractor: MediaOutputInteractor, - audioRepository: AudioRepository, audioModeInteractor: AudioModeInteractor, ) { @@ -50,13 +47,12 @@ constructor( 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 @@ constructor( } if (!isOngoingCall) { - addCall(communicationDevice?.type) + addStream(AudioManager.STREAM_VOICE_CALL) } addStream(AudioManager.STREAM_RING) @@ -80,14 +76,6 @@ constructor( } .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 521f608878cb..ffb1f11c4970 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 @@ constructor( 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 @@ constructor( 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 @@ constructor( 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/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index 113a8c05ee66..5e37d4cd1faf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.hardware.display.DisplayManager; +import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; import android.testing.TestableLooper; @@ -80,6 +81,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { private AccessibilityManager mAccessibilityManager; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private AccessibilityFloatingMenuController mController; + private TestableLooper mTestableLooper; @Mock private AccessibilityButtonTargetsObserver mTargetsObserver; @Mock @@ -108,6 +110,7 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false); mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); + mTestableLooper = TestableLooper.get(this); when(mTargetsObserver.getCurrentAccessibilityButtonTargets()) .thenReturn(Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(), @@ -231,7 +234,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { mKeyguardCallback.onKeyguardVisibilityChanged(false); mKeyguardCallback.onUserSwitching(fakeUserId); - mKeyguardCallback.onUserSwitchComplete(fakeUserId); + mController.mUserInitializationCompleteCallback.onUserInitializationComplete(1); + mTestableLooper.processAllMessages(); assertThat(mController.mFloatingMenu).isNotNull(); } @@ -346,7 +350,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { new AccessibilityFloatingMenuController(mContextWrapper, windowManager, viewCaptureAwareWindowManager, displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings, - displayTracker, mNavigationModeController); + displayTracker, mNavigationModeController, new Handler( + mTestableLooper.getLooper())); controller.init(); return controller; diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt index 6aecc0e2b8fc..4883d1bb9400 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt @@ -63,6 +63,7 @@ import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -365,7 +366,7 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { testScope.runTest { underTest.start() - underTest.onQsExpansionStared() + underTest.onQsExpansionStarted() runCurrent() assertThat(faceAuthRepository.runningAuthRequest.value) @@ -373,6 +374,79 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() { } @Test + @EnableSceneContainer + fun faceAuthIsRequestedWhenQuickSettingsIsExpandedToTheShade() = + testScope.runTest { + underTest.start() + faceAuthRepository.canRunFaceAuth.value = true + kosmos.sceneInteractor.snapToScene(toScene = Scenes.QuickSettings, "for-test") + runCurrent() + + kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test") + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, + currentScene = flowOf(Scenes.QuickSettings), + progress = MutableStateFlow(0.2f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)) + } + + @Test + @EnableSceneContainer + fun faceAuthIsRequestedOnlyOnceWhenQuickSettingsIsExpandedToTheShade() = + testScope.runTest { + underTest.start() + faceAuthRepository.canRunFaceAuth.value = true + kosmos.sceneInteractor.snapToScene(toScene = Scenes.QuickSettings, "for-test") + runCurrent() + + kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test") + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, + currentScene = flowOf(Scenes.QuickSettings), + progress = MutableStateFlow(0.2f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + ) + ) + + runCurrent() + assertThat(faceAuthRepository.runningAuthRequest.value) + .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)) + faceAuthRepository.runningAuthRequest.value = null + + // expansion progress shouldn't trigger face auth again + kosmos.sceneInteractor.setTransitionState( + MutableStateFlow( + ObservableTransitionState.Transition( + fromScene = Scenes.QuickSettings, + toScene = Scenes.Shade, + currentScene = flowOf(Scenes.QuickSettings), + progress = MutableStateFlow(0.5f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + ) + ) + + assertThat(faceAuthRepository.runningAuthRequest.value).isNull() + } + + @Test fun faceAuthIsRequestedWhenNotificationPanelClicked() = testScope.runTest { underTest.start() diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt index 0ac04b61f13d..76539d776d3d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt @@ -467,6 +467,16 @@ class DisplayRepositoryTest : SysuiTestCase() { assertThat(values.toIdSets()).containsExactly(setOf(0, 1, 2)) } + @Test + fun displayFlow_onlyDefaultDisplayAvailable_neverEmitsEmptySet() = + testScope.runTest { + setDisplays(0) + + val values: List<Set<Display>> by collectValues(displayRepository.displays) + + assertThat(values.toIdSets()).containsExactly(setOf(0)) + } + private fun Iterable<Display>.ids(): List<Int> = map { it.displayId } private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() } 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 4e1b12f70da5..43c7ed6a769d 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.ext.junit.runners.AndroidJUnit4 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 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { @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 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { 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 73b9f5783004..07f7557d965a 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.data.repository.KeyguardQuickAffordanceRepo 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.testDispatcher 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 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @Mock private lateinit var glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel - @Mock private lateinit var transitionInteractor: KeyguardTransitionInteractor private val kosmos = testKosmos() @@ -163,8 +168,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { // 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 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { intendedAlphaMutableStateFlow.value = 1f intendedShadeAlphaMutableStateFlow.value = 0f - intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha) .thenReturn(intendedAlphaMutableStateFlow) whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow()) @@ -283,8 +285,6 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { whenever(glanceableHubToLockscreenTransitionViewModel.shortcutsAlpha) .thenReturn(emptyFlow()) whenever(shadeInteractor.anyExpansion).thenReturn(intendedShadeAlphaMutableStateFlow) - whenever(transitionInteractor.finishedKeyguardState) - .thenReturn(intendedFinishedKeyguardStateFlow) underTest = KeyguardQuickAffordancesCombinedViewModel( @@ -334,7 +334,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { lockscreenToPrimaryBouncerTransitionViewModel, lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel, - transitionInteractor = transitionInteractor, + transitionInteractor = kosmos.keyguardTransitionInteractor, ) } @@ -776,7 +776,10 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @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 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { @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/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt index 19735e29834e..8435b1cb71dc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt @@ -25,9 +25,10 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger -import com.android.settingslib.notification.data.repository.FakeZenModeRepository import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost @@ -41,16 +42,15 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig import com.android.systemui.res.R -import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository -import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor +import com.android.systemui.statusbar.policy.data.repository.zenModeRepository +import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.After @@ -59,7 +59,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations -import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @@ -68,6 +67,9 @@ import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class ModesTileTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val testDispatcher = kosmos.testDispatcher @Mock private lateinit var qsHost: QSHost @@ -85,17 +87,10 @@ class ModesTileTest : SysuiTestCase() { @Mock private lateinit var dialogDelegate: ModesDialogDelegate - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) - private val inputHandler = FakeQSTileIntentUserInputHandler() - private val zenModeRepository = FakeZenModeRepository() + private val zenModeRepository = kosmos.zenModeRepository private val tileDataInteractor = - ModesTileDataInteractor( - context, - ZenModeInteractor(context, zenModeRepository, mock<NotificationSettingsRepository>()), - testDispatcher - ) + ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher) private val mapper = ModesTileMapper( context.orCreateTestableResources diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java index a8d5008c61c6..eb1a04d8e4ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java @@ -308,6 +308,40 @@ public final class AppClipsActivityTest extends SysuiTestCase { assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNotNull(); } + @Test + @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS) + public void appClipsLaunched_backlinks_multipleBacklinksAvailable_duplicateName() + throws RemoteException { + // Set up mocking for multiple backlinks. + ResolveInfo resolveInfo1 = createBacklinksTaskResolveInfo(); + + ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo(); + RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask(); + int taskId2 = BACKLINKS_TASK_ID + 2; + runningTaskInfo2.taskId = taskId2; + + when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false), + mDisplayIdCaptor.capture())) + .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS, runningTaskInfo2)); + when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(resolveInfo1, + resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2); + when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE); + + // Using same AssistContent data for both tasks. + mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, BACKLINKS_TASK_ID); + mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, taskId2); + + // Mocking complete, trigger backlinks. + launchActivity(); + waitForIdleSync(); + + // Verify default backlink shown to user has the numerical suffix. + TextView backlinksData = mActivity.findViewById(R.id.backlinks_data); + assertThat(backlinksData.getText().toString()).isEqualTo( + mContext.getString(R.string.backlinks_duplicate_label_format, + BACKLINKS_TASK_APP_NAME, 1)); + } + private void setUpMocksForBacklinks() throws RemoteException { when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false), mDisplayIdCaptor.capture())) 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 5a5cdcd99054..3ba1447eb406 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -61,7 +61,6 @@ import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.controller.keyguardMediaController import com.android.systemui.res.R import com.android.systemui.scene.shared.model.sceneDataSourceDelegator -import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController @@ -417,13 +416,17 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { // 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() } } @@ -702,7 +705,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { verify(containerView).onTouchEvent(DOWN_EVENT) // User is interacting with shade on lockscreen. - fakeShadeRepository.setLegacyLockscreenShadeTracking(true) + shadeTestUtil.setLockscreenShadeTracking(true) testableLooper.processAllMessages() // A move event is ignored while the user is already interacting. @@ -716,6 +719,30 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @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. + shadeTestUtil.setShadeExpansion(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 523d15c7548f..9481e5a52098 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.NotificationWakeUpCoordinator 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.NotificationListContain 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 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { 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 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { 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 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { 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 905cc4cd13b2..a7fd1609d1ca 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.ext.junit.runners.AndroidJUnit4; 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 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo } @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 64eadb7db1e3..90655c3cf4b3 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.KEYGUARD 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 @@ class NotificationPanelViewControllerWithCoroutinesTest : 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 000000000000..593f87382b52 --- /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 ce79fbde77a3..7bc6d4ae2816 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 @@ class CallChipViewModelTest : SysuiTestCase() { ) 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 @@ class CallChipViewModelTest : SysuiTestCase() { ) 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 @@ class CallChipViewModelTest : SysuiTestCase() { 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 a8d2c5b4cdd7..77992dbaecc2 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 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { 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 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { 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 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { 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 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { 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 000000000000..8576893216d0 --- /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 804eb5cf597c..16101bfe387c 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 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { 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 a2ef59916ff6..791a21d0fb63 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 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { 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 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { 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 a724cfaa4798..4977c548fb92 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 @@ class ChipTransitionHelperTest : SysuiTestCase() { } 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 556ec6a307ab..bd5df07b7ece 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 @@ package com.android.systemui.statusbar.chips.ui.viewmodel 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.model.ScreenRecordModel 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.OngoingCall 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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { 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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) + kosmos.demoRonChipViewModel.start() + whenever(kosmos.packageManager.getApplicationIcon(any<String>())) + .thenReturn(BitmapDrawable()) } @Test @@ -169,15 +183,24 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } @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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } @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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { // 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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { 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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { 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 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { 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 a4ce3abd6b9e..a18de68dfcfc 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.footer.ui.view.FooterView; 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; @@ -1273,7 +1272,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpDisappearEvent_setsHeadsUpAnimatingAway() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1289,7 +1288,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @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); @@ -1308,7 +1307,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpDisappearEvent_pendingAppearEvent_headsUpAnimatingAwayNotSet() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1326,7 +1325,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME) + @EnableSceneContainer public void testGenerateHeadsUpAppearEvent_headsUpAnimatingAwayNotSet() { // GIVEN NSSL is ready for HUN animations Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class); @@ -1362,7 +1361,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @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/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt index 76dc65cbc915..2ed34735db1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt @@ -34,6 +34,7 @@ import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.statusbar.StatusBarIcon import com.android.settingslib.notification.modes.TestModeBuilder import com.android.systemui.Flags import com.android.systemui.SysuiTestCase @@ -41,6 +42,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State +import com.android.systemui.kosmos.testScope import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.screenrecord.RecordingController @@ -71,9 +73,7 @@ import com.android.systemui.util.time.DateFormatUtil import com.android.systemui.util.time.FakeSystemClock import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -145,7 +145,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { private lateinit var alarmCallbackCaptor: ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback> - private val testScope = TestScope(UnconfinedTestDispatcher()) + private val testScope = kosmos.testScope private val fakeConnectedDisplayStateProvider = FakeConnectedDisplayStateProvider() private val zenModeController = FakeZenModeController() @@ -249,7 +249,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) @@ -261,7 +261,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED) + fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED) + runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, false) } @@ -272,9 +273,12 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) - fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) + runCurrent() + fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED) + runCurrent() + fakeConnectedDisplayStateProvider.setState(State.CONNECTED) + runCurrent() inOrder(iconController).apply { verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) @@ -289,7 +293,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { statusBarPolicy.init() clearInvocations(iconController) - fakeConnectedDisplayStateProvider.emit(State.CONNECTED_SECURE) + fakeConnectedDisplayStateProvider.setState(State.CONNECTED_SECURE) + runCurrent() verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true) } @@ -390,7 +395,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS) + @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun zenModeInteractorActiveModeChanged_showsModeIcon() = testScope.runTest { statusBarPolicy.init() @@ -403,8 +408,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { .setName("Bedtime Mode") .setType(AutomaticZenRule.TYPE_BEDTIME) .setActive(true) - .setPackage("some.package") - .setIconResId(123) + .setPackage(mContext.packageName) + .setIconResId(android.R.drawable.ic_lock_lock) .build(), TestModeBuilder() .setId("other") @@ -412,7 +417,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { .setType(AutomaticZenRule.TYPE_OTHER) .setActive(true) .setPackage(SystemZenRules.PACKAGE_ANDROID) - .setIconResId(456) + .setIconResId(android.R.drawable.ic_media_play) .build(), ) ) @@ -422,17 +427,25 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController) .setResourceIcon( eq(ZEN_SLOT), - eq("some.package"), - eq(123), - eq(null), - eq("Bedtime Mode") + eq(mContext.packageName), + eq(android.R.drawable.ic_lock_lock), + any(), // non-null + eq("Bedtime Mode"), + eq(StatusBarIcon.Shape.FIXED_SPACE) ) zenModeRepository.deactivateMode("bedtime") runCurrent() verify(iconController) - .setResourceIcon(eq(ZEN_SLOT), eq(null), eq(456), eq(null), eq("Other Mode")) + .setResourceIcon( + eq(ZEN_SLOT), + eq(null), + eq(android.R.drawable.ic_media_play), + any(), // non-null + eq("Other Mode"), + eq(StatusBarIcon.Shape.FIXED_SPACE) + ) zenModeRepository.deactivateMode("other") runCurrent() @@ -441,7 +454,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_UI_ICONS) + @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun zenModeControllerOnGlobalZenChanged_doesNotUpdateDndIcon() { statusBarPolicy.init() reset(iconController) @@ -450,7 +463,8 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any()) verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any()) - verify(iconController, never()).setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any()) + verify(iconController, never()) + .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any()) } @Test @@ -466,7 +480,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any()) verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any()) verify(iconController, never()) - .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any()) + .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any()) } @Test @@ -529,9 +543,11 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { } private class FakeConnectedDisplayStateProvider : ConnectedDisplayInteractor { - private val flow = MutableSharedFlow<State>() + private val flow = MutableStateFlow(State.DISCONNECTED) - suspend fun emit(value: State) = flow.emit(value) + fun setState(value: State) { + flow.value = value + } override val connectedDisplayState: Flow<State> get() = flow 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 54c03e894804..3e3c046ce62e 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.dreams.DreamOverlayStateController; 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 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { 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 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { 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/phone/ui/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt new file mode 100644 index 000000000000..90732d0183d2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt @@ -0,0 +1,122 @@ +/* + * 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.phone.ui + +import android.app.Flags +import android.graphics.drawable.Icon +import android.os.UserHandle +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.statusbar.StatusBarIcon +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider +import com.android.systemui.statusbar.phone.StatusBarLocation +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter +import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter +import com.android.systemui.util.Assert +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.RETURNS_DEEP_STUBS +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class IconManagerTest : SysuiTestCase() { + + private lateinit var underTest: IconManager + private lateinit var viewGroup: ViewGroup + + @Before + fun setUp() { + Assert.setTestThread(Thread.currentThread()) + viewGroup = LinearLayout(context) + underTest = + IconManager( + viewGroup, + StatusBarLocation.HOME, + mock<WifiUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS), + mock<MobileUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS), + mock<MobileContextProvider>(defaultAnswer = RETURNS_DEEP_STUBS), + ) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) + fun addIcon_shapeWrapContent_addsIconViewWithVariableWidth() { + val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.WRAP_CONTENT) + + underTest.addIcon(0, "slot", false, sbIcon) + + assertThat(viewGroup.childCount).isEqualTo(1) + val iconView = viewGroup.getChildAt(0) as StatusBarIconView + assertThat(iconView).isNotNull() + + assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER) + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) + fun addIcon_shapeFixedSpace_addsIconViewWithFixedWidth() { + val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE) + + underTest.addIcon(0, "slot", false, sbIcon) + + assertThat(viewGroup.childCount).isEqualTo(1) + val iconView = viewGroup.getChildAt(0) as StatusBarIconView + assertThat(iconView).isNotNull() + + assertThat(iconView.layoutParams.width).isNotEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + assertThat(iconView.layoutParams.width).isEqualTo(iconView.layoutParams.height) + assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.FIT_CENTER) + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI_ICONS) + fun addIcon_iconsFlagOff_addsIconViewWithVariableWidth() { + val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE) + + underTest.addIcon(0, "slot", false, sbIcon) + + assertThat(viewGroup.childCount).isEqualTo(1) + val iconView = viewGroup.getChildAt(0) as StatusBarIconView + assertThat(iconView).isNotNull() + + assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER) + } + + private fun newStatusBarIcon(shape: StatusBarIcon.Shape) = + StatusBarIcon( + UserHandle.CURRENT, + context.packageName, + Icon.createWithResource(context, android.R.drawable.ic_media_next), + 0, + 0, + "", + StatusBarIcon.Type.ResourceIcon, + shape, + ) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt index 26a57e4c1ca9..50a13b93ea15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt @@ -424,7 +424,14 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS) fun setResourceIcon_setsIconAndPreloadedIconInHolder() { val drawable = ColorDrawable(1) - underTest.setResourceIcon("slot", "some.package", 123, drawable, "description") + underTest.setResourceIcon( + "slot", + "some.package", + 123, + drawable, + "description", + StatusBarIcon.Shape.FIXED_SPACE + ) val iconHolder = iconList.getIconHolder("slot", 0) assertThat(iconHolder).isNotNull() @@ -432,6 +439,7 @@ class StatusBarIconControllerImplTest : SysuiTestCase() { assertThat(iconHolder?.icon?.icon?.resId).isEqualTo(123) assertThat(iconHolder?.icon?.icon?.resPackage).isEqualTo("some.package") assertThat(iconHolder?.icon?.contentDescription).isEqualTo("description") + assertThat(iconHolder?.icon?.shape).isEqualTo(StatusBarIcon.Shape.FIXED_SPACE) assertThat(iconHolder?.icon?.preloadedIcon).isEqualTo(drawable) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 76982ae12516..6de2caa59dd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -28,7 +28,6 @@ import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.vcn.VcnTransportInfo import android.net.wifi.WifiInfo import android.net.wifi.WifiManager -import android.os.Bundle import android.os.ParcelUuid import android.telephony.CarrierConfigManager import android.telephony.ServiceState @@ -56,7 +55,6 @@ import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository @@ -74,7 +72,6 @@ import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.android.wifitrackerlib.MergedCarrierEntry import com.android.wifitrackerlib.WifiEntry @@ -98,6 +95,7 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @OptIn(ExperimentalCoroutinesApi::class) @@ -602,47 +600,85 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { @SuppressLint("UnspecifiedRegisterReceiverFlag") @Test - fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() = + fun testDeviceEmergencyCallState_eagerlyChecksState() = testScope.runTest { - // Value starts out empty (null) - assertThat(underTest.deviceServiceState.value).isNull() + // Value starts out false + assertThat(underTest.isDeviceEmergencyCallCapable.value).isFalse() + whenever(telephonyManager.activeModemCount).thenReturn(1) + whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { _ -> + ServiceState().apply { isEmergencyOnly = true } + } // WHEN an appropriate intent gets sent out - val intent = serviceStateIntent(subId = -1, emergencyOnly = false) + val intent = serviceStateIntent(subId = -1) fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, intent, ) runCurrent() - // THEN the repo's state is updated - val expected = ServiceStateModel(isEmergencyOnly = false) - assertThat(underTest.deviceServiceState.value).isEqualTo(expected) + // THEN the repo's state is updated despite no listeners + assertThat(underTest.isDeviceEmergencyCallCapable.value).isEqualTo(true) } @Test - fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() = + fun testDeviceEmergencyCallState_aggregatesAcrossSlots_oneTrue() = testScope.runTest { - // device based state tracks -1 - val intent = serviceStateIntent(subId = -1, emergencyOnly = false) + val latest by collectLastValue(underTest.isDeviceEmergencyCallCapable) + + // GIVEN there are multiple slots + whenever(telephonyManager.activeModemCount).thenReturn(4) + // GIVEN only one of them reports ECM + whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { invocation -> + when (invocation.getArgument(0) as Int) { + 0 -> ServiceState().apply { isEmergencyOnly = false } + 1 -> ServiceState().apply { isEmergencyOnly = false } + 2 -> ServiceState().apply { isEmergencyOnly = true } + 3 -> ServiceState().apply { isEmergencyOnly = false } + else -> null + } + } + + // GIVEN a broadcast goes out for the appropriate subID + val intent = serviceStateIntent(subId = -1) fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, intent, ) runCurrent() - val deviceBasedState = ServiceStateModel(isEmergencyOnly = false) - assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState) + // THEN the device is in ECM, because one of the service states is + assertThat(latest).isTrue() + } + + @Test + fun testDeviceEmergencyCallState_aggregatesAcrossSlots_allFalse() = + testScope.runTest { + val latest by collectLastValue(underTest.isDeviceEmergencyCallCapable) + + // GIVEN there are multiple slots + whenever(telephonyManager.activeModemCount).thenReturn(4) + // GIVEN only one of them reports ECM + whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { invocation -> + when (invocation.getArgument(0) as Int) { + 0 -> ServiceState().apply { isEmergencyOnly = false } + 1 -> ServiceState().apply { isEmergencyOnly = false } + 2 -> ServiceState().apply { isEmergencyOnly = false } + 3 -> ServiceState().apply { isEmergencyOnly = false } + else -> null + } + } - // ... and ignores any other subId - val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true) + // GIVEN a broadcast goes out for the appropriate subID + val intent = serviceStateIntent(subId = -1) fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( context, - intent2, + intent, ) runCurrent() - assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState) + // THEN the device is in ECM, because one of the service states is + assertThat(latest).isFalse() } @Test @@ -1549,15 +1585,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { */ private fun serviceStateIntent( subId: Int, - emergencyOnly: Boolean = false, ): Intent { - val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly } - - val bundle = Bundle() - serviceState.fillInNotifierBundle(bundle) - return Intent(Intent.ACTION_SERVICE_STATE).apply { - putExtras(bundle) putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index cc0eae7e6fea..e218fba1d07a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt @@ -29,7 +29,6 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.log.table.TableLogBuffer -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository @@ -897,13 +896,11 @@ class MobileIconsInteractorTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode) - connectionsRepository.deviceServiceState.value = - ServiceStateModel(isEmergencyOnly = true) + connectionsRepository.isDeviceEmergencyCallCapable.value = true assertThat(latest).isTrue() - connectionsRepository.deviceServiceState.value = - ServiceStateModel(isEmergencyOnly = false) + connectionsRepository.isDeviceEmergencyCallCapable.value = false assertThat(latest).isFalse() } 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 f486787e9cc7..0945742fb325 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.Network 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE") assertThat(latest).containsExactly(ConnectivitySlot.WIFI) - - job.cancel() } @Test @@ -181,23 +170,21 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { ) assertThat(latest).isEmpty() - - job.cancel() } @Test @@ -219,29 +204,25 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { assertThat(latest!!.mobile.isDefault).isTrue() assertThat(latest!!.carrierMerged.isDefault).isTrue() assertThat(latest!!.wifi.isDefault).isTrue() + } + + /** 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) - job.cancel() + 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.wifi.isDefault).isFalse() - - job.cancel() } @Test @@ -531,8 +517,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { @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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest!!.isValidated).isFalse() - job.cancel() } @Test @@ -669,8 +647,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isEqualTo(SUB_1_ID) - job.cancel() } @Test @@ -689,8 +665,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isNull() - job.cancel() } @Test @@ -728,8 +700,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { // 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities) assertThat(latest).isNull() - - job.cancel() } @Test @@ -862,6 +829,7 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { } @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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { } @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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 @@ class ConnectivityRepositoryImplTest : SysuiTestCase() { 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 12cfdcfa8df5..e396b567ac89 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.domain.interactor.sceneInteractor 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 @@ class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCa } @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 caa177908db0..1e2648b228f3 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 @@ public class VolumeDialogImplTest extends SysuiTestCase { 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/settingslib/notification/modes/ZenIconLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt new file mode 100644 index 000000000000..8541d7704517 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt @@ -0,0 +1,23 @@ +/* + * 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.settingslib.notification.modes + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.google.common.util.concurrent.MoreExecutors + +val Kosmos.zenIconLoader by Fixture { ZenIconLoader(MoreExecutors.newDirectExecutorService()) } 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 110797191d37..c252924f4d2d 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_DEVICE_ENTRY_UDFPS_REFACTOR 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 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER 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/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt index e96aeada0212..5753c6c6e8ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt @@ -27,7 +27,6 @@ import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterNotNull @SysUISingleton @@ -37,38 +36,41 @@ class FakeDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceA private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null) override val authenticationStatus: Flow<FaceAuthenticationStatus> = _authenticationStatus.filterNotNull() + fun setAuthenticationStatus(status: FaceAuthenticationStatus) { _authenticationStatus.value = status } + private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null) override val detectionStatus: Flow<FaceDetectionStatus> get() = _detectionStatus.filterNotNull() + fun setDetectionStatus(status: FaceDetectionStatus) { _detectionStatus.value = status } private val _isLockedOut = MutableStateFlow(false) override val isLockedOut = _isLockedOut - private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null) - val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> = - _runningAuthRequest.asStateFlow() + val runningAuthRequest: MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?> = + MutableStateFlow(null) private val _isAuthRunning = MutableStateFlow(false) override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning override val isBypassEnabled = MutableStateFlow(false) + override fun setLockedOut(isLockedOut: Boolean) { _isLockedOut.value = isLockedOut } override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) { - _runningAuthRequest.value = uiEvent to fallbackToDetection + runningAuthRequest.value = uiEvent to fallbackToDetection _isAuthRunning.value = true } override fun cancel() { _isAuthRunning.value = false - _runningAuthRequest.value = null + runningAuthRequest.value = null } } 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 000000000000..82a5311269f0 --- /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 c6b5ed0b608f..007d2297e387 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 @@ val Kosmos.keyguardEnabledInteractor by 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 8e8f4b69e401..aa94c368e8f1 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 @@ val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by 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 a05e60672ef7..4196e54a085d 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 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.occludedToDozingTransitionViewModel by Fixture { OccludedToDozingTransitionViewModel( + deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt index cce3eb02023b..c0d65a076ca0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.android.systemui.statusbar.pipeline.mobile.data.model +package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel -import android.telephony.ServiceState +import android.content.packageManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.commandline.commandRegistry +import com.android.systemui.util.time.fakeSystemClock -/** - * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to - * extract from service state here for consumption downstream - */ -data class ServiceStateModel(val isEmergencyOnly: Boolean) { - companion object { - fun fromServiceState(serviceState: ServiceState): ServiceStateModel { - return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly) - } +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 16e288fcf113..5382c1c4b8d0 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.Kosmos 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 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by 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 000000000000..14777b4bc492 --- /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/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt index 8229575a128f..e7be639cf92a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt @@ -23,7 +23,6 @@ import com.android.settingslib.SignalIcon import com.android.settingslib.mobile.MobileMappings import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.log.table.TableLogBuffer -import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy @@ -94,9 +93,10 @@ class FakeMobileConnectionsRepository( private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON) override val defaultMobileIconGroup = _defaultMobileIconGroup - override val deviceServiceState = MutableStateFlow<ServiceStateModel?>(null) + override val isDeviceEmergencyCallCapable = MutableStateFlow(false) override val isAnySimSecure = MutableStateFlow(false) + override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value private var isInEcmMode: Boolean = false diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt index 66be7e7a7a7e..61b53c9a2067 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorKosmos.kt @@ -17,8 +17,10 @@ package com.android.systemui.statusbar.policy.domain.interactor import android.content.testableContext +import com.android.settingslib.notification.modes.zenIconLoader import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository import com.android.systemui.statusbar.policy.data.repository.zenModeRepository @@ -27,5 +29,7 @@ val Kosmos.zenModeInteractor by Fixture { context = testableContext, zenModeRepository = zenModeRepository, notificationSettingsRepository = notificationSettingsRepository, + bgDispatcher = testDispatcher, + iconLoader = zenIconLoader, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index 2dbac670b298..0089199cfb88 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -64,7 +64,8 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> @Override public void setResourceIcon(String slot, @Nullable String resPackage, int iconResId, - @Nullable Drawable preloadedIcon, CharSequence contentDescription) { + @Nullable Drawable preloadedIcon, CharSequence contentDescription, + StatusBarIcon.Shape shape) { } @Override 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 63386d016a7a..dd5bbf37c0f1 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 @@ package com.android.systemui.volume.panel.component.volume.domain.interactor 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 @@ val Kosmos.audioSlidersInteractor by AudioSlidersInteractor( applicationCoroutineScope, mediaOutputInteractor, - audioRepository, audioModeInteractor, ) } diff --git a/ravenwood/.gitignore b/ravenwood/.gitignore new file mode 100644 index 000000000000..751553b3acb9 --- /dev/null +++ b/ravenwood/.gitignore @@ -0,0 +1 @@ +*.bak diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING index 7e2ee3e2a052..469759bc4b40 100644 --- a/ravenwood/TEST_MAPPING +++ b/ravenwood/TEST_MAPPING @@ -1,17 +1,12 @@ -// 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" }, { "name": "hoststubgentest" }, + { "name": "hoststubgen-test-tiny-test" }, { "name": "hoststubgen-invoke-test" }, - { - "name": "RavenwoodMockitoTest_device" - }, - { - "name": "RavenwoodBivalentTest_device" - }, + { "name": "RavenwoodMockitoTest_device" }, + { "name": "RavenwoodBivalentTest_device" }, + // The sysui tests should match vendor/unbundled_google/packages/SystemUIGoogle/TEST_MAPPING { "name": "SystemUIGoogleTests", @@ -39,12 +34,71 @@ } ], "ravenwood-presubmit": [ + // AUTO-GENERATED-START + // DO NOT MODIFY MANUALLY + // Use scripts/update-test-mapping.sh to update it. { - "name": "RavenwoodMinimumTest", + "name": "AdServicesSharedLibrariesUnitTestsRavenwood", "host": true }, { - "name": "RavenwoodMockitoTest", + "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 }, { @@ -52,12 +106,65 @@ "host": true }, { - "name": "RavenwoodResApkTest", + "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 + }, + { + "name": "RavenwoodMockitoTest", + "host": true + }, + { + "name": "RavenwoodResApkTest", + "host": true + }, + { + "name": "RavenwoodRuntimeTest", + "host": true + }, + { + "name": "RavenwoodServicesTest", + "host": true + }, + { + "name": "SystemUiRavenTests", + "host": true } + // AUTO-GENERATED-END ] } diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java index 8dadd398ad55..09a0aa8dbaa2 100644 --- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java @@ -64,7 +64,7 @@ public class CallTracker { /** * Check the number of calls stored in {@link #mNumCalled}. */ - protected void assertCalls(Object... methodNameAndCountPairs) { + public void assertCalls(Object... methodNameAndCountPairs) { // Create a local copy HashMap<String, Integer> counts = new HashMap<>(mNumCalled); for (int i = 0; i < methodNameAndCountPairs.length - 1; i += 2) { @@ -95,7 +95,7 @@ public class CallTracker { * Same as {@link #assertCalls(Object...)} but it kills the process if it fails. * Only use in @AfterClass. */ - protected void assertCallsOrDie(Object... methodNameAndCountPairs) { + public void assertCallsOrDie(Object... methodNameAndCountPairs) { try { assertCalls(methodNameAndCountPairs); } catch (Throwable th) { diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java new file mode 100644 index 000000000000..9d878f444e5e --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java @@ -0,0 +1,49 @@ +/* + * 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.ravenwoodtest.bivalenttest.ravenizer; + +import android.platform.test.annotations.NoRavenizer; +import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; + +import org.junit.Test; + +/** + * Test for {@link android.platform.test.annotations.NoRavenizer} + */ +@NoRavenizer +public class RavenwoodNoRavenizerTest { + public static final String TAG = "RavenwoodNoRavenizerTest"; + + private static final CallTracker sCallTracker = new CallTracker(); + + /** + * With @NoRavenizer, this method shouldn't be called. + */ + @RavenwoodTestRunnerInitializing + public static void ravenwoodRunnerInitializing() { + sCallTracker.incrementMethodCallCount(); + } + + /** + * Make sure ravenwoodRunnerInitializing() wasn't called. + */ + @Test + public void testNotRavenized() { + sCallTracker.assertCalls( + "ravenwoodRunnerInitializing", 0 + ); + } +} diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java new file mode 100644 index 000000000000..7e396c2080eb --- /dev/null +++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java @@ -0,0 +1,69 @@ +/* + * 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.ravenwoodtest.bivalenttest.ravenizer; + +import android.util.Log; + +import org.junit.AfterClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +/** + * Test to make sure {@link Suite} works with the ravenwood test runner. + */ +@RunWith(Suite.class) +@Suite.SuiteClasses({ + RavenwoodSuiteTest.Test1.class, + RavenwoodSuiteTest.Test2.class +}) +public class RavenwoodSuiteTest { + public static final String TAG = "RavenwoodSuiteTest"; + + private static final CallTracker sCallTracker = new CallTracker(); + + @AfterClass + public static void afterClass() { + Log.i(TAG, "afterClass called"); + + sCallTracker.assertCallsOrDie( + "test1", 1, + "test2", 1 + ); + } + + /** + * Workaround for the issue where tradefed won't think a class is a test class + * if it has a @RunWith but no @Test methods, even if it is a Suite. + */ + @Test + public void testEmpty() { + } + + public static class Test1 { + @Test + public void test1() { + sCallTracker.incrementMethodCallCount(); + } + } + + public static class Test2 { + @Test + public void test2() { + sCallTracker.incrementMethodCallCount(); + } + } +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java index 1da93eba94f7..f237ba908507 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java @@ -57,7 +57,8 @@ public class RavenwoodAwareTestRunnerHook { */ public static void onRunnerInitializing(Runner runner, TestClass testClass) { // This log call also ensures the framework JNI is loaded. - Log.i(TAG, "onRunnerInitializing: testClass=" + testClass + " runner=" + runner); + Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass() + + " runner=" + runner); // TODO: Move the initialization code to a better place. diff --git a/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java b/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java new file mode 100644 index 000000000000..a84f16f619d3 --- /dev/null +++ b/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java @@ -0,0 +1,36 @@ +/* + * 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.platform.test.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Disable the ravenizer preprocessor for a class. This should be only used for testing + * ravenizer itself, or to workaround issues with the preprocessor. A test class probably won't run + * properly if it's not preprocessed. + * + * @hide + */ +@Inherited +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface NoRavenizer { +} diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java index 2b55ac52ab75..7d991663f4b1 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -21,10 +21,13 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood; 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; import org.junit.Assume; +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runner.Runner; @@ -36,8 +39,10 @@ import org.junit.runner.manipulation.Orderable; 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.RunnerBuilder; import org.junit.runners.model.Statement; import org.junit.runners.model.TestClass; @@ -51,8 +56,6 @@ import java.lang.reflect.InvocationTargetException; /** * A test runner used for Ravenwood. * - * TODO: Handle ENABLE_PROBE_IGNORED - * * It will delegate to another runner specified with {@link InnerRunner} * (default = {@link BlockJUnit4ClassRunner}) with the following features. * - Add a {@link RavenwoodAwareTestRunnerHook#onRunnerInitializing} hook, which is called before @@ -134,12 +137,15 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde 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) { - RavenwoodCommonUtils.log(TAG, "[" + getTestClass() + " @" + this + "] " + message); + RavenwoodCommonUtils.log(TAG, "[" + getTestClass().getJavaClass() + " @" + this + "] " + + message); } private Error logAndFail(String message, Throwable innerException) { @@ -149,45 +155,76 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde } 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; - } + try { + mTestClass = new TestClass(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; + } - // 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; - } + // Find the real runner. + final Class<? extends Runner> realRunnerClass; + final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class); + if (innerRunnerAnnotation != null) { + realRunnerClass = innerRunnerAnnotation.value(); + } else { + // Default runner. + realRunnerClass = BlockJUnit4ClassRunner.class; + } - onRunnerInitializing(); + onRunnerInitializing(); - try { - log("Initializing the inner runner: " + realRunner); + try { + log("Initializing the inner runner: " + realRunnerClass); - mRealRunner = realRunner.getConstructor(Class.class).newInstance(testClass); + mRealRunner = instantiateRealRunner(realRunnerClass, testClass); + mDescription = mRealRunner.getDescription(); - } catch (InstantiationException | IllegalAccessException - | InvocationTargetException | NoSuchMethodException e) { - throw logAndFail("Failed to instantiate " + realRunner, e); + } catch (InstantiationException | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { + throw logAndFail("Failed to instantiate " + realRunnerClass, 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; + } + } + } + + private static Runner instantiateRealRunner( + Class<? extends Runner> realRunnerClass, + Class<?> testClass) + throws NoSuchMethodException, InvocationTargetException, InstantiationException, + IllegalAccessException { + try { + return realRunnerClass.getConstructor(Class.class).newInstance(testClass); + } catch (NoSuchMethodException e) { + var runnerBuilder = new AllDefaultPossibilitiesBuilder(); + return realRunnerClass.getConstructor(Class.class, + RunnerBuilder.class).newInstance(testClass, runnerBuilder); } } @@ -202,7 +239,7 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde log("onRunnerInitializing"); - RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClsas); + RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass); // Hook point to allow more customization. runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null); @@ -230,7 +267,7 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde @Override public Description getDescription() { - return mRealRunner.getDescription(); + return mDescription; } @Override @@ -241,6 +278,10 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde return; } + if (maybeReportExceptionFromConstructor(notifier)) { + return; + } + sCurrentRunner.set(this); try { runWithHooks(getDescription(), Scope.Runner, Order.First, @@ -250,6 +291,18 @@ public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orde } } + /** 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 000000000000..478503b699a0 --- /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 fb9b823ee93b..05f3fdffdaa7 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 000000000000..be15b711b980 --- /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 000000000000..b6cf5b857682 --- /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 d8366c58c50d..34239b826c67 100644 --- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt @@ -46,6 +46,7 @@ android.util.EmptyArray 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.ColorSpace 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 3a7fab39e4ac..0dcd271562d1 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 e92ef7216e25..a38512ec9f2d 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 @@ data class RavenizerStats( /** 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 @@ data class RavenizerStats( RavenizerStats{ totalTime=$totalTime, loadStructureTime=$loadStructureTime, + validationTime=$validationTime, totalProcessTime=$totalProcessTime, totalConversionTime=$totalConversionTime, totalCopyTime=$totalCopyTime, @@ -84,16 +88,44 @@ data class RavenizerStats( 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 e85e3be31b77..e8341e5ceb06 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 @@ class RavenizerOptions( /** 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 @@ class RavenizerOptions( "--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 @@ class RavenizerOptions( 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 e026e7ab3679..1aa70c08c254 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 @@ -15,10 +15,12 @@ */ package com.android.platform.test.ravenwood.ravenizer +import android.platform.test.annotations.NoRavenizer import android.platform.test.ravenwood.RavenwoodAwareTestRunner import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.asm.findAnyAnnotation import com.android.hoststubgen.asm.startsWithAny +import com.android.hoststubgen.asm.toHumanReadableClassName import org.junit.rules.TestRule import org.junit.runner.RunWith import org.objectweb.asm.Type @@ -30,6 +32,7 @@ data class TypeHolder( val desc = type.descriptor val descAsSet = setOf<String>(desc) val internlName = type.internalName + val humanReadableName = type.internalName.toHumanReadableClassName() } val testAnotType = TypeHolder(org.junit.Test::class.java) @@ -37,6 +40,7 @@ val ruleAnotType = TypeHolder(org.junit.Rule::class.java) val classRuleAnotType = TypeHolder(org.junit.ClassRule::class.java) val runWithAnotType = TypeHolder(RunWith::class.java) val innerRunnerAnotType = TypeHolder(RavenwoodAwareTestRunner.InnerRunner::class.java) +val noRavenizerAnotType = TypeHolder(NoRavenizer::class.java) val testRuleType = TypeHolder(TestRule::class.java) val ravenwoodTestRunnerType = TypeHolder(RavenwoodAwareTestRunner::class.java) @@ -87,9 +91,12 @@ fun String.shouldByBypassed(): Boolean { 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 000000000000..27092d28ae5e --- /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 25cad0213b72..eaef2cf6a956 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 @@ -30,6 +30,7 @@ import com.android.platform.test.ravenwood.ravenizer.RavenizerInternalException import com.android.platform.test.ravenwood.ravenizer.classRuleAnotType import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass import com.android.platform.test.ravenwood.ravenizer.innerRunnerAnotType +import com.android.platform.test.ravenwood.ravenizer.noRavenizerAnotType import com.android.platform.test.ravenwood.ravenizer.ravenwoodTestRunnerType import com.android.platform.test.ravenwood.ravenizer.ruleAnotType import com.android.platform.test.ravenwood.ravenizer.runWithAnotType @@ -183,7 +184,7 @@ class RunnerRewritingAdapter private constructor( av.visit("value", ravenwoodTestRunnerType.type) av.visitEnd() } - log.d("Processed ${classInternalName.toHumanReadableClassName()}") + log.i("Update the @RunWith: ${classInternalName.toHumanReadableClassName()}") } /* @@ -435,7 +436,19 @@ class RunnerRewritingAdapter private constructor( companion object { fun shouldProcess(classes: ClassNodes, className: String): Boolean { - return isTestLookingClass(classes, className) + if (!isTestLookingClass(classes, className)) { + return false + } + // Don't process a class if it has a @NoRavenizer annotation. + classes.findClass(className)?.let { cn -> + if (cn.findAnyAnnotation(noRavenizerAnotType.descAsSet) != null) { + log.w("Class ${className.toHumanReadableClassName()} has" + + " @${noRavenizerAnotType.humanReadableName}. Skipping." + ) + return false + } + } + return true } fun maybeApply( diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 7cbb97e56b01..f1a8b5a96080 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -163,6 +163,7 @@ import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import android.view.accessibility.IAccessibilityManager; import android.view.accessibility.IAccessibilityManagerClient; import android.view.accessibility.IMagnificationConnection; +import android.view.accessibility.IUserInitializationCompleteCallback; import android.view.inputmethod.EditorInfo; import com.android.internal.R; @@ -366,6 +367,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables = new ArrayList<>(); + @VisibleForTesting + final HashSet<IUserInitializationCompleteCallback> + mUserInitializationCompleteCallbacks = + new HashSet<IUserInitializationCompleteCallback>(); + @GuardedBy("mLock") private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM; @@ -2034,6 +2040,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this), WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS); } + + for (IUserInitializationCompleteCallback callback + : mUserInitializationCompleteCallbacks) { + try { + callback.onUserInitializationComplete(mCurrentUserId); + } catch (RemoteException re) { + Log.e("AccessibilityManagerService", + "Error while dispatching userInitializationComplete callback: ", + re); + } + } } } @@ -6251,6 +6268,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + @RequiresNoPermission + public void registerUserInitializationCompleteCallback( + IUserInitializationCompleteCallback callback) { + synchronized (mLock) { + mUserInitializationCompleteCallbacks.add(callback); + } + } + + @Override + @RequiresNoPermission + public void unregisterUserInitializationCompleteCallback( + IUserInitializationCompleteCallback callback) { + synchronized (mLock) { + mUserInitializationCompleteCallbacks.remove(callback); + } + } + + @Override @EnforcePermission(INJECT_EVENTS) public void injectInputEventToInputFilter(InputEvent event) { injectInputEventToInputFilter_enforcePermission(); diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java index 53885fc5799e..f5d07ef850c8 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java @@ -94,36 +94,39 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { targetUser = mCallerValidator.verifyTargetUserHandle( requestInternal.getUserHandle(), validatedCallingPackage); } catch (SecurityException exception) { - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse - .Builder(ExecuteAppFunctionResponse.RESULT_DENIED, - getExceptionMessage(exception)).build()); + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse + .newFailure(ExecuteAppFunctionResponse.RESULT_DENIED, + exception.getMessage(), + /*extras=*/ null)); return; } // TODO(b/354956319): Add and honor the new enterprise policies. if (mCallerValidator.isUserOrganizationManaged(targetUser)) { - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse.Builder( + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure( ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR, - "Cannot run on a device with a device owner or from the managed profile." - ).build()); + "Cannot run on a device with a device owner or from the managed profile.", + /*extras=*/ null + )); return; } String targetPackageName = requestInternal.getClientRequest().getTargetPackageName(); if (TextUtils.isEmpty(targetPackageName)) { - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse.Builder( + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure( ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT, - "Target package name cannot be empty." - ).build()); + "Target package name cannot be empty.", + /*extras=*/ null + )); return; } if (!mCallerValidator.verifyCallerCanExecuteAppFunction( validatedCallingPackage, targetPackageName)) { - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse - .Builder(ExecuteAppFunctionResponse.RESULT_DENIED, - "Caller does not have permission to execute the appfunction") - .build()); + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse + .newFailure(ExecuteAppFunctionResponse.RESULT_DENIED, + "Caller does not have permission to execute the appfunction", + /*extras=*/ null)); return; } @@ -131,10 +134,11 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { targetPackageName, targetUser); if (serviceIntent == null) { - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse.Builder( + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure( ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR, - "Cannot find the target service." - ).build()); + "Cannot find the target service.", + /*extras=*/ null + )); return; } bindAppFunctionServiceUnchecked(requestInternal, serviceIntent, targetUser, @@ -171,9 +175,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { } ); } catch (Exception e) { - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse - .Builder(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR, - getExceptionMessage(e)).build()); + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse + .newFailure(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR, + e.getMessage(), + /*extras=*/ null)); serviceUsageCompleteListener.onCompleted(); } } @@ -181,33 +186,32 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { @Override public void onFailedToConnect() { Slog.e(TAG, "Failed to connect to service"); - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse - .Builder(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR, - "Failed to connect to AppFunctionService").build()); + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse + .newFailure(ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR, + "Failed to connect to AppFunctionService", + /*extras=*/ null)); } @Override public void onTimedOut() { Slog.e(TAG, "Timed out"); safeExecuteAppFunctionCallback.onResult( - new ExecuteAppFunctionResponse.Builder( + ExecuteAppFunctionResponse.newFailure( ExecuteAppFunctionResponse.RESULT_TIMED_OUT, - "Binding to AppFunctionService timed out." - ).build()); + "Binding to AppFunctionService timed out.", + /*extras=*/ null + )); } } ); if (!bindServiceResult) { Slog.e(TAG, "Failed to bind to the AppFunctionService"); - safeExecuteAppFunctionCallback.onResult(new ExecuteAppFunctionResponse.Builder( + safeExecuteAppFunctionCallback.onResult(ExecuteAppFunctionResponse.newFailure( ExecuteAppFunctionResponse.RESULT_TIMED_OUT, - "Failed to bind the AppFunctionService." - ).build()); + "Failed to bind the AppFunctionService.", + /*extras=*/ null + )); } } - - private String getExceptionMessage(Exception exception) { - return exception.getMessage() == null ? "" : exception.getMessage(); - } } 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 000000000000..c01fe311e9ca --- /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 2bfdd0a7c819..77650ebe2698 100644 --- a/services/companion/Android.bp +++ b/services/companion/Android.bp @@ -28,7 +28,6 @@ java_library_static { ], 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 4678a165b83f..5fd282df97ec 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 @@ public class ObservableUuidStore { } 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 66313e6ca957..000000000000 --- 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 b0bacfd158ed..fed153f0b7fa 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 @@ final class VirtualDeviceLog { 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 @@ final class VirtualDeviceLog { 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 @@ final class VirtualDeviceLog { 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 616f5d09e13f..000000000000 --- 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/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 47203fbf2ef0..fbe593fd3df1 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -20,6 +20,8 @@ import static android.content.Intent.ACTION_REBOOT; import static android.content.Intent.ACTION_SHUTDOWN; import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig; +import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecoveryEvents; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; @@ -44,6 +46,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.IndentingPrintWriter; import android.util.LongArrayQueue; import android.util.Slog; import android.util.Xml; @@ -51,7 +54,6 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -72,6 +74,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -1265,18 +1268,21 @@ public class PackageWatchdog { /** Dump status of every observer in mAllObservers. */ - public void dump(IndentingPrintWriter pw) { - pw.println("Package Watchdog status"); - pw.increaseIndent(); + public void dump(PrintWriter pw) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println("Package Watchdog status"); + ipw.increaseIndent(); synchronized (mLock) { for (String observerName : mAllObservers.keySet()) { - pw.println("Observer name: " + observerName); - pw.increaseIndent(); + ipw.println("Observer name: " + observerName); + ipw.increaseIndent(); ObserverInternal observerInternal = mAllObservers.get(observerName); - observerInternal.dump(pw); - pw.decreaseIndent(); + observerInternal.dump(ipw); + ipw.decreaseIndent(); } } + ipw.decreaseIndent(); + dumpCrashRecoveryEvents(ipw); } @VisibleForTesting diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index bba97fad0fc9..cadceb52dedd 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -18,7 +18,7 @@ package com.android.server; import static android.provider.DeviceConfig.Properties; -import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; +import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; import android.annotation.IntDef; import android.annotation.NonNull; @@ -291,13 +291,13 @@ public class RescueParty { Properties properties = new Properties.Builder(namespaceToReset).build(); try { if (!DeviceConfig.setProperties(properties)) { - logCriticalInfo(Log.ERROR, "Failed to clear properties under " + logCrashRecoveryEvent(Log.ERROR, "Failed to clear properties under " + namespaceToReset + ". Running `device_config get_sync_disabled_for_tests` will confirm" + " if config-bulk-update is enabled."); } } catch (DeviceConfig.BadConfigException exception) { - logCriticalInfo(Log.WARN, "namespace " + namespaceToReset + logCrashRecoveryEvent(Log.WARN, "namespace " + namespaceToReset + " is already banned, skip reset."); } } @@ -528,7 +528,7 @@ public class RescueParty { if (!TextUtils.isEmpty(failedPackage)) { successMsg += " for package " + failedPackage; } - logCriticalInfo(Log.DEBUG, successMsg); + logCrashRecoveryEvent(Log.DEBUG, successMsg); } catch (Throwable t) { logRescueException(level, failedPackage, t); } @@ -687,7 +687,7 @@ public class RescueParty { if (!TextUtils.isEmpty(failedPackageName)) { failureMsg += " for package " + failedPackageName; } - logCriticalInfo(Log.ERROR, failureMsg + ": " + msg); + logCrashRecoveryEvent(Log.ERROR, failureMsg + ": " + msg); } private static int mapRescueLevelToUserImpact(int rescueLevel) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d80b38e32b6c..d12153559b31 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 @@ public class ActivityManagerService extends IActivityManager.Stub boolean closeFd = true; try { - synchronized (mProcLock) { - if (fd == null) { - throw new IllegalArgumentException("null fd"); - } - mBinderTransactionTrackingEnabled = false; + Objects.requireNonNull(fd); + + record ProcessToDump(String processName, IApplicationThread thread) { } - PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor())); - pw.println("Binder transaction traces for all processes.\n"); - mProcessList.forEachLruProcessesLOSP(true, process -> { + 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; } + 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: " + process.processName); - pw.flush(); + pw.println("Traces for process: " + processName); + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); 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(); + thread.stopBinderTrackingAndDump(tp.getWriteFd()); + tp.go(fd.getFileDescriptor()); + } finally { + tp.kill(); } - }); - closeFd = false; - return true; + } 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 b738482ba66c..168ec052e67d 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 @@ public class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub // 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 @@ public class AudioService extends IAudioService.Stub @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/crashrecovery/CrashRecoveryUtils.java b/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java new file mode 100644 index 000000000000..3eb3380a57a0 --- /dev/null +++ b/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java @@ -0,0 +1,85 @@ +/* + * 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.crashrecovery; + +import android.os.Environment; +import android.util.IndentingPrintWriter; +import android.util.Slog; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.time.ZoneId; + +/** + * Class containing helper methods for the CrashRecoveryModule. + * + * @hide + */ +public class CrashRecoveryUtils { + private static final String TAG = "CrashRecoveryUtils"; + private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 1000 * 1000; // ~1MB + private static final Object sFileLock = new Object(); + + /** Persist recovery related events in crashrecovery events file.**/ + public static void logCrashRecoveryEvent(int priority, String msg) { + Slog.println(priority, TAG, msg); + try { + File fname = getCrashRecoveryEventsFile(); + synchronized (sFileLock) { + FileOutputStream out = new FileOutputStream(fname, true); + PrintWriter pw = new PrintWriter(out); + String dateString = LocalDateTime.now(ZoneId.systemDefault()).toString(); + pw.println(dateString + ": " + msg); + pw.close(); + } + } catch (IOException e) { + Slog.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage()); + } + } + + /** Dump recovery related events from crashrecovery events file.**/ + public static void dumpCrashRecoveryEvents(IndentingPrintWriter pw) { + pw.println("CrashRecovery Events: "); + pw.increaseIndent(); + final File file = getCrashRecoveryEventsFile(); + final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE; + synchronized (sFileLock) { + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + if (skipSize > 0) { + in.skip(skipSize); + } + String line; + while ((line = in.readLine()) != null) { + pw.println(line); + } + } catch (IOException e) { + Slog.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage()); + } + } + pw.decreaseIndent(); + } + + private static File getCrashRecoveryEventsFile() { + File systemDir = new File(Environment.getDataDirectory(), "system"); + return new File(systemDir, "crashrecovery-events.txt"); + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index bbe7b2b038c9..3c7b9d37e4c6 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 @@ public class HdmiControlService extends SystemService { 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 e3061a7c39ae..41351613331d 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 @@ public class BackgroundUserSoundNotifier { 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 95e5b84a8ed3..2bc6d53147fb 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 @@ public class UserRestrictionsUtils { 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; + } + 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 !IMMUTABLE_BY_OWNERS.contains(restriction) - && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction) - && !(!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)); + return true; } /** diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java index f518769bdb35..e9cb279439a6 100644 --- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java +++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java @@ -375,24 +375,34 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe @Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation, @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl) { - if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) { - final int shellUid = UserHandle.getUid( - UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID); - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(code, - new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, - attributionSource.getAttributionTag(), - attributionSource.getToken(), /*renouncedPermissions*/ null, - attributionSource.getDeviceId(), attributionSource.getNext()), - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - skiProxyOperation); - } finally { - Binder.restoreCallingIdentity(identity); - } + if (!isDelegateOp(code)) { + return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, + message, shouldCollectMessage, skiProxyOperation); + } + + final int shellUid = UserHandle.getUid( + UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID); + AttributionSource next = attributionSource.getNext(); + if (next != null && next.getUid() == mDelegateAndOwnerUid) { + next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null, + next.getDeviceId(), next.getNext()); + attributionSource = new AttributionSource(attributionSource, next); + } + if (attributionSource.getUid() == mDelegateAndOwnerUid) { + attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG, + attributionSource.getAttributionTag(), + attributionSource.getToken(), /*renouncedPermissions*/ null, + attributionSource.getDeviceId(), attributionSource.getNext()); + } + final long identity = Binder.clearCallingIdentity(); + try { + return superImpl.apply(code, attributionSource, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + skiProxyOperation); + } finally { + Binder.restoreCallingIdentity(identity); } - return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, - message, shouldCollectMessage, skiProxyOperation); } @Override diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 1c786e668c7a..68026ea9094a 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -18,6 +18,8 @@ package com.android.server.rollback; import static android.content.pm.Flags.provideInfoOfApkInApex; +import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; + import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +42,7 @@ import android.os.PowerManager; import android.os.SystemProperties; import android.sysprop.CrashRecoveryProperties; import android.util.ArraySet; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -532,11 +535,13 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage, @FailureReasons int rollbackReason) { assertInWorkerThread(); + String failedPackageName = (failedPackage == null ? null : failedPackage.getPackageName()); Slog.i(TAG, "Rolling back package. RollbackId: " + rollback.getRollbackId() - + " failedPackage: " - + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failedPackage: " + failedPackageName + " rollbackReason: " + rollbackReason); + logCrashRecoveryEvent(Log.DEBUG, String.format("Rolling back %s. Reason: %s", + failedPackageName, rollbackReason)); final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason); final String failedPackageToLog; @@ -724,6 +729,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } Slog.i(TAG, "Rolling back all available low impact rollbacks"); + logCrashRecoveryEvent(Log.DEBUG, "Rolling back all available. Reason: " + rollbackReason); // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all // pending staged rollbacks are handled. for (RollbackInfo rollback : lowImpactRollbacks) { 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 440d2514537c..eb5361c84b89 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 @@ import java.util.Set; * @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 @@ public class CasResource { 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 class CasResource { */ 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 31149f3590b8..5cef729627f0 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 @@ public final class CiCamResource extends CasResource { * 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 0afb049d31c7..9229f7f016bc 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde @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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde @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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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; - } + // Grant frontend when there is unused resource. + if (grantingFrontend != null) { + updateFrontendClientMappingOnNewGrant(grantingFrontend.getHandle(), + request.clientId); + frontendHandle[0] = grantingFrontend.getHandle(); + 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; + // 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; } - frontendHandle[0] = inUseLowestPriorityFrHandle; - updateFrontendClientMappingOnNewGrant( - inUseLowestPriorityFrHandle, request.clientId); - return true; } return false; @@ -1192,165 +1171,257 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @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 + ")"); } + int[] reclaimOwnerId = new int[1]; + if (!claimLnb(request, lnbHandle, reclaimOwnerId)) { + return false; + } + if (lnbHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) { + return false; + } + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], + TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) { + return false; + } + 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; - 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()); + 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 (grantingLnbHandle > -1) { - lnbHandle[0] = grantingLnbHandle; - updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId); - return true; - } + // 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 (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE - && ((requestClient.getPriority() > currentLowestPriority) || ( - (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) { - if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(), - TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) { - return false; + // 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; } - lnbHandle[0] = inUseLowestPriorityLnbHandle; - updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId); - 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()); + if (reclaimOwnerId[0] != INVALID_CLIENT_ID) { + if (!reclaimResource(reclaimOwnerId[0], + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { + return false; + } + synchronized (mLock) { + if (getCasResource(request.casSystemId).isFullyUsed()) { + Slog.e(TAG, "Reclaimed cas still fully used"); + return false; + } + updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); } } + return true; + } - // 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, - TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { - return false; + 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; } - casSessionHandle[0] = generateResourceHandle( - TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); - updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId); - 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @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 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @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; + int[] reclaimOwnerId = new int[1]; + if (!claimDemux(request, demuxHandle, reclaimOwnerId)) { + return false; } - - demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - ClientProfile requestClient = getClientProfile(request.clientId); - - if (requestClient == null) { + 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(); - - // 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) { - int numOfSupportedCaps = dr.getNumOfCaps(); - boolean shouldUpdate = false; - // update lowest priority - if (currentLowestPriority > priority) { - currentLowestPriority = priority; - isRequestFromSameProcess = (requestClient.getProcessId() - == (getClientProfile(dr.getOwnerClientId())).getProcessId()); + 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); + } + + // 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; + } - // reset the smallest caps when lower priority resource is found - smallestNumOfSupportedCapsInUse = numOfSupportedCaps; + 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(); - 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) { + // look for the demux with minimum caps supporting the desired caps + if (smallestNumOfSupportedCaps > numOfSupportedCaps) { + smallestNumOfSupportedCaps = numOfSupportedCaps; + grantingDemux = dr; + } + } 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; } - } - if (shouldUpdate) { - inUseLowestPriorityDrHandle = dr.getHandle(); } } } } - } - // Grant demux when there is unused resource. - if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { - demuxHandle[0] = grantingDemuxHandle; - updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId); - return true; - } + // Grant demux when there is unused resource. + if (grantingDemux != null) { + updateDemuxClientMappingOnNewGrant(grantingDemux.getHandle(), request.clientId); + demuxHandle[0] = grantingDemux.getHandle(); + 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; + // 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; } - demuxHandle[0] = inUseLowestPriorityDrHandle; - updateDemuxClientMappingOnNewGrant( - inUseLowestPriorityDrHandle, request.clientId); - return true; } return false; @@ -1792,7 +1918,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde return; } - mListeners.put(clientId, record); + synchronized (mLock) { + mListeners.put(clientId, record); + } } @VisibleForTesting @@ -1808,33 +1936,44 @@ public class TunerResourceManagerService extends SystemService implements IBinde // 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; + synchronized (mLock) { + ClientProfile profile = getClientProfile(reclaimingClientId); + if (profile == null) { + return true; + } + shareFeClientIds = profile.getShareFeClientIds(); } - Set<Integer> shareFeClientIds = profile.getShareFeClientIds(); + ResourcesReclaimListenerRecord listenerRecord = null; 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; + 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); + } } - clearAllResourcesAndClientMapping(getClientProfile(clientId)); } 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); + } + if (listenerRecord != null) { + try { + listenerRecord.getListener().onReclaimResources(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e); + } } - clearAllResourcesAndClientMapping(profile); + return true; } @@ -2258,6 +2397,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 @@ public class TunerResourceManagerService extends SystemService implements IBinde 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 000000000000..b4d3862431f1 --- /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 fe0cf5909970..ea4bd0182ea3 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 @@ final class HalVibration extends Vibration { @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 @@ final class HalVibration extends Vibration { // 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 @@ final class HalVibration extends Vibration { 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 @@ final class HalVibration extends Vibration { ? 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 4e58b9a35243..83e05f4eff9f 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.util.SparseArray; 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 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener { * * @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 aa4b9f3e9d7d..9a0479337471 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.os.vibrator.VibrationEffectSegment; 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 @@ abstract class Vibration { 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 final int mProtoEnumValue; - - Status(int value) { - mProtoEnumValue = value; - } - - public int getProtoEnumValue() { - return mProtoEnumValue; - } - } + private VibrationSession.Status mStatus; - 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; } - /** Return true if vibration is a repeating vibration. */ - abstract boolean isRepeating(); + VibrationSession.Status getStatus() { + return mStatus; + } + + /** Return true is current status is different from {@link VibrationSession.Status#RUNNING}. */ + boolean hasEnded() { + return mStatus != VibrationSession.Status.RUNNING; + } /** - * 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. + * Set the {@link VibrationSession} of this vibration and reports the current system time as + * this vibration end time, for debugging purposes. * - * 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}. + * <p>This method will only accept given value if the current status is {@link + * VibrationSession.Status#RUNNING}. */ - static 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; + 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); + } - @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); - } + /** Return true if vibration is a repeating vibration. */ + abstract boolean isRepeating(); - @Override - public int hashCode() { - return Objects.hash(attrs, uid, deviceId, opPkg, reason); - } + /** Return {@link VibrationSession.DebugInfo} with read-only debug data about this vibration. */ + abstract VibrationSession.DebugInfo getDebugInfo(); - @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 @@ abstract class Vibration { * 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 @@ abstract class Vibration { 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 @@ abstract class Vibration { } @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 @@ abstract class Vibration { + ", 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 @@ abstract class Vibration { 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 @@ abstract class Vibration { 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 000000000000..5640b49b28d8 --- /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 0d6778c18759..69cdcf47680d 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.GuardedBy; 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 @@ final class VibrationSettings { /** * 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 @@ final class VibrationSettings { * * @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 @@ final class VibrationSettings { // 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 @@ final class VibrationSettings { * {@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 8179d6aea9ca..fc0c6e7bf05e 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 @@ final class VibrationStats { * @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 @@ final class VibrationStats { * <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 @@ final class VibrationStats { 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 7152844cc772..5137d1938332 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.Slog; 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 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } /** - * 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 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { } // 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 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { 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 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { 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 cfb4c74fcbfc..ab4a4d8fc08d 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 android.util.Slog; 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 @@ final class VibrationThread extends Thread { runCurrentVibrationWithWakeLockAndDeathLink(); } finally { clientVibrationCompleteIfNotAlready( - new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED)); + new Vibration.EndInfo(Status.FINISHED_UNEXPECTED)); } } finally { mWakeLock.release(); @@ -259,7 +260,7 @@ final class VibrationThread extends Thread { } 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 de5e662f2a71..3a814cd80836 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 @@ final class VibratorControlService extends IVibratorControlService.Stub { } /** - * 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 c143bebdd4a5..799934af54c0 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.GuardedBy; 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } if (mCurrentExternalVibration != null && shouldCancelVibration( - mCurrentExternalVibration.externalVibration.getVibrationAttributes(), + mCurrentExternalVibration.getCallerInfo().attrs, usageFilter)) { - mCurrentExternalVibration.mute(); + mCurrentExternalVibration.notifyEnded(); endExternalVibrateLocked( cancelledByUserInfo, /* continueExternalControl= */ false); } @@ -860,7 +857,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { : 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } - @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { * {@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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { /** 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { * 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { */ 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } } - /** 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } /** - * 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { 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 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } 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 ba2594abd4d4..f53dda6ee35b 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_INFO; 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 @@ public class WallpaperManagerService extends IWallpaperManager.Stub 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 @@ public class WallpaperManagerService extends IWallpaperManager.Stub 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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 235a2115a964..ebdf52cc9037 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -813,6 +813,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** The last set {@link DropInputMode} for this activity surface. */ @DropInputMode private int mLastDropInputMode = DropInputMode.NONE; + /** Whether the input to this activity will be dropped during the current playing animation. */ + private boolean mIsInputDroppedForAnimation; /** * Whether the application has desk mode resources. Calculated and cached when @@ -1647,6 +1649,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + /** Sets if all input will be dropped as a protection during the client-driven animation. */ + void setDropInputForAnimation(boolean isInputDroppedForAnimation) { + if (mIsInputDroppedForAnimation == isInputDroppedForAnimation) { + return; + } + mIsInputDroppedForAnimation = isInputDroppedForAnimation; + updateUntrustedEmbeddingInputProtection(); + } + /** * Sets to drop input when obscured to activity if it is embedded in untrusted mode. * @@ -1659,7 +1670,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (getSurfaceControl() == null) { return; } - if (isEmbeddedInUntrustedMode()) { + if (mIsInputDroppedForAnimation) { + // Disable all input during the animation. + setDropInputMode(DropInputMode.ALL); + } else if (isEmbeddedInUntrustedMode()) { // Set drop input to OBSCURED when untrusted embedded. setDropInputMode(DropInputMode.OBSCURED); } else { diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 197bd5a308cd..ab02d49e5f96 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -97,6 +97,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -253,10 +254,12 @@ public class AppTransitionController { getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */); final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity); - // No AE remote animation with Shell transition. - // Unfreeze the windows that were previously frozen for TaskFragment animation. - unfreezeEmbeddedChangingWindows(); - overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); + // Check if there is any override + if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) { + // Unfreeze the windows that were previously frozen for TaskFragment animation. + unfreezeEmbeddedChangingWindows(); + overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); + } final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps) || containsVoiceInteraction(mDisplayContent.mOpeningApps); @@ -687,6 +690,64 @@ public class AppTransitionController { } /** + * Overrides the pending transition with the remote animation defined by the + * {@link ITaskFragmentOrganizer} if all windows in the transition are children of + * {@link TaskFragment} that are organized by the same organizer. + * + * @return {@code true} if the transition is overridden. + */ + private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit, + ArraySet<Integer> activityTypes) { + if (transitionMayContainNonAppWindows(transit)) { + return false; + } + if (!transitionContainsTaskFragmentWithBoundsOverride()) { + // No need to play TaskFragment remote animation if all embedded TaskFragment in the + // transition fill the Task. + return false; + } + + final Task task = findParentTaskForAllEmbeddedWindows(); + final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task); + final RemoteAnimationDefinition definition = organizer != null + ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController + .getRemoteAnimationDefinition(organizer) + : null; + final RemoteAnimationAdapter adapter = definition != null + ? definition.getAdapter(transit, activityTypes) + : null; + if (adapter == null) { + return false; + } + mDisplayContent.mAppTransition.overridePendingAppTransitionRemote( + adapter, false /* sync */, true /*isActivityEmbedding*/); + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, + "Override with TaskFragment remote animation for transit=%s", + AppTransition.appTransitionOldToString(transit)); + + final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController + .getTaskFragmentOrganizerUid(organizer); + final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding( + organizerUid); + final RemoteAnimationController remoteAnimationController = + mDisplayContent.mAppTransition.getRemoteAnimationController(); + if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) { + // We are going to use client-driven animation, Disable all input on activity windows + // during the animation (unless it is fully trusted) to ensure it is safe to allow + // client to animate the surfaces. + // This is needed for all activity windows in the animation Task. + remoteAnimationController.setOnRemoteAnimationReady(() -> { + final Consumer<ActivityRecord> updateActivities = + activity -> activity.setDropInputForAnimation(true); + task.forAllActivities(updateActivities); + }); + ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment." + + " Disabled all input during TaskFragment remote animation.", task.mTaskId); + } + return true; + } + + /** * Overrides the pending transition with the remote animation defined for the transition in the * set of defined remote animations in the app window token. */ diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index f8665c70c61d..432089ff2fcf 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -53,6 +53,7 @@ import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.function.Consumer; /** * Helper class to run app animations in a remote process. @@ -348,6 +349,10 @@ class RemoteAnimationController implements DeathRecipient { } finally { mIsFinishing = false; } + // Reset input for all activities when the remote animation is finished. + final Consumer<ActivityRecord> updateActivities = + activity -> activity.setDropInputForAnimation(false); + mDisplayContent.forAllActivities(updateActivities); } setRunningRemoteAnimation(false); ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation"); diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index e4a31760e5ed..5aa34d22f00f 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -46,6 +46,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; +import android.view.RemoteAnimationDefinition; import android.view.WindowManager; import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizerController; @@ -156,6 +157,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr private final boolean mIsSystemOrganizer; /** + * {@link RemoteAnimationDefinition} for embedded activities transition animation that is + * organized by this organizer. + */ + @Nullable + private RemoteAnimationDefinition mRemoteAnimationDefinition; + + /** * Map from {@link TaskFragmentTransaction#getTransactionToken()} to the * {@link Transition#getSyncId()} that has been deferred. {@link TransitionController} will * wait until the organizer finished handling the {@link TaskFragmentTransaction}. @@ -592,6 +600,50 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr } @Override + public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, + @NonNull RemoteAnimationDefinition definition) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Register remote animations for organizer=%s uid=%d pid=%d", + organizer.asBinder(), uid, pid); + final TaskFragmentOrganizerState organizerState = + mTaskFragmentOrganizerState.get(organizer.asBinder()); + if (organizerState == null) { + throw new IllegalStateException("The organizer hasn't been registered."); + } + if (organizerState.mRemoteAnimationDefinition != null) { + throw new IllegalStateException( + "The organizer has already registered remote animations=" + + organizerState.mRemoteAnimationDefinition); + } + + definition.setCallingPidUid(pid, uid); + organizerState.mRemoteAnimationDefinition = definition; + } + } + + @Override + public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer) { + final int pid = Binder.getCallingPid(); + final long uid = Binder.getCallingUid(); + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, + "Unregister remote animations for organizer=%s uid=%d pid=%d", + organizer.asBinder(), uid, pid); + final TaskFragmentOrganizerState organizerState = + mTaskFragmentOrganizerState.get(organizer.asBinder()); + if (organizerState == null) { + Slog.e(TAG, "The organizer hasn't been registered."); + return; + } + + organizerState.mRemoteAnimationDefinition = null; + } + } + + @Override public void setSavedState(@NonNull ITaskFragmentOrganizer organizer, @Nullable Bundle state) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -649,6 +701,25 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr } } + /** + * Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns + * {@code null} if it doesn't. + */ + @Nullable + public RemoteAnimationDefinition getRemoteAnimationDefinition( + @NonNull ITaskFragmentOrganizer organizer) { + synchronized (mGlobalLock) { + final TaskFragmentOrganizerState organizerState = + mTaskFragmentOrganizerState.get(organizer.asBinder()); + if (organizerState == null) { + Slog.e(TAG, "TaskFragmentOrganizer has been unregistered or died when trying" + + " to play animation on its organized windows."); + return null; + } + return organizerState.mRemoteAnimationDefinition; + } + } + int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) { final TaskFragmentOrganizerState state = validateAndGetState(organizer); return state.mOrganizerUid; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 6bfa32a97ddb..7f6dc8472813 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { * 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } // 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { 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/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 979b3a5697cf..e3ceb3348de7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7920,7 +7920,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean allWindowsDrawn = false; synchronized (mGlobalLock) { - if (displayId == DEFAULT_DISPLAY + if ((displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) && mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) { // Use the ready-to-play of transition as the signal. return; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index af4a48da7e0e..886ae7ad7e50 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { }); } + 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ @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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 19a942cd2eed..24ee46fbcd6f 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 @@ final class PolicyDefinition<V> { 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/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java new file mode 100644 index 000000000000..6f38fcae182c --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java @@ -0,0 +1,145 @@ +/* + * 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.crashrecovery; + + + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.quality.Strictness.LENIENT; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.os.Environment; +import android.util.IndentingPrintWriter; +import android.util.Log; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.runner.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoSession; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + + +/** + * Test CrashRecovery Utils. + */ +@RunWith(AndroidJUnit4.class) +public class CrashRecoveryUtilsTest { + + private MockitoSession mStaticMockSession; + private final String mLogMsg = "Logging from test"; + private final String mCrashrecoveryEventTag = "CrashRecovery Events: "; + private File mCacheDir; + + @Before + public void setup() throws IOException { + Context context = ApplicationProvider.getApplicationContext(); + mCacheDir = context.getCacheDir(); + mStaticMockSession = ExtendedMockito.mockitoSession() + .spyStatic(Environment.class) + .strictness(LENIENT) + .startMocking(); + ExtendedMockito.doReturn(mCacheDir).when(() -> Environment.getDataDirectory()); + + createCrashRecoveryEventsTempDir(); + } + + @After + public void tearDown() throws IOException { + mStaticMockSession.finishMocking(); + deleteCrashRecoveryEventsTempFile(); + } + + @Test + public void testCrashRecoveryUtils() { + testLogCrashRecoveryEvent(); + testDumpCrashRecoveryEvents(); + } + + @Test + public void testDumpCrashRecoveryEventsWithoutAnyLogs() { + assertThat(getCrashRecoveryEventsTempFile().exists()).isFalse(); + StringWriter sw = new StringWriter(); + IndentingPrintWriter ipw = new IndentingPrintWriter(sw, " "); + CrashRecoveryUtils.dumpCrashRecoveryEvents(ipw); + ipw.close(); + + String dump = sw.getBuffer().toString(); + assertThat(dump).contains(mCrashrecoveryEventTag); + assertThat(dump).doesNotContain(mLogMsg); + } + + private void testLogCrashRecoveryEvent() { + assertThat(getCrashRecoveryEventsTempFile().exists()).isFalse(); + CrashRecoveryUtils.logCrashRecoveryEvent(Log.WARN, mLogMsg); + + assertThat(getCrashRecoveryEventsTempFile().exists()).isTrue(); + String fileContent = null; + try { + File file = getCrashRecoveryEventsTempFile(); + FileInputStream fis = new FileInputStream(file); + byte[] data = new byte[(int) file.length()]; + fis.read(data); + fis.close(); + fileContent = new String(data, StandardCharsets.UTF_8); + } catch (Exception e) { + fail("Unable to read the events file"); + } + assertThat(fileContent).contains(mLogMsg); + } + + private void testDumpCrashRecoveryEvents() { + StringWriter sw = new StringWriter(); + IndentingPrintWriter ipw = new IndentingPrintWriter(sw, " "); + CrashRecoveryUtils.dumpCrashRecoveryEvents(ipw); + ipw.close(); + + String dump = sw.getBuffer().toString(); + assertThat(dump).contains(mCrashrecoveryEventTag); + assertThat(dump).contains(mLogMsg); + } + + private void createCrashRecoveryEventsTempDir() throws IOException { + Files.deleteIfExists(getCrashRecoveryEventsTempFile().toPath()); + File mMockDirectory = new File(mCacheDir, "system"); + if (!mMockDirectory.exists()) { + assertThat(mMockDirectory.mkdir()).isTrue(); + } + } + + private void deleteCrashRecoveryEventsTempFile() throws IOException { + Files.deleteIfExists(getCrashRecoveryEventsTempFile().toPath()); + } + + private File getCrashRecoveryEventsTempFile() { + File systemTempDir = new File(mCacheDir, "system"); + return new File(systemTempDir, "crashrecovery-events.txt"); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index c8cbbb5957ec..2e6c93cb92aa 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -102,6 +102,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityWindowAttributes; import android.view.accessibility.IAccessibilityManager; +import android.view.accessibility.IUserInitializationCompleteCallback; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; @@ -137,6 +138,7 @@ import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.mockito.internal.util.reflection.FieldReader; import org.mockito.internal.util.reflection.FieldSetter; import org.mockito.stubbing.Answer; @@ -209,6 +211,7 @@ public class AccessibilityManagerServiceTest { @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController; @Mock private ProxyManager mProxyManager; @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback; @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor; private IAccessibilityManager mA11yManagerServiceOnDevice; private AccessibilityServiceConnection mAccessibilityServiceConnection; @@ -2042,6 +2045,36 @@ public class AccessibilityManagerServiceTest { .isEqualTo(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); } + @Test + public void registerUserInitializationCompleteCallback_isRegistered() { + mA11yms.mUserInitializationCompleteCallbacks.clear(); + + mA11yms.registerUserInitializationCompleteCallback(mUserInitializationCompleteCallback); + + assertThat(mA11yms.mUserInitializationCompleteCallbacks).containsExactly( + mUserInitializationCompleteCallback); + } + + @Test + public void unregisterUserInitializationCompleteCallback_isUnregistered() { + mA11yms.mUserInitializationCompleteCallbacks.clear(); + mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback); + + mA11yms.unregisterUserInitializationCompleteCallback(mUserInitializationCompleteCallback); + + assertThat(mA11yms.mUserInitializationCompleteCallbacks).isEmpty(); + } + + @Test + public void switchUser_callsUserInitializationCompleteCallback() throws RemoteException { + mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback); + + mA11yms.switchUser(UserHandle.MIN_SECONDARY_USER_ID); + + verify(mUserInitializationCompleteCallback).onUserInitializationComplete( + UserHandle.MIN_SECONDARY_USER_ID); + } + private Set<String> readStringsFromSetting(String setting) { final Set<String> result = new ArraySet<>(); mA11yms.readColonDelimitedSettingToSet( 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 b4cc3434e013..698bda335f83 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 com.android.server.wm.ActivityTaskManagerInternal; import java.io.File; import java.io.IOException; +import java.util.List; import java.util.Map; /** @@ -325,6 +326,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @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 b7483d67563c..cb4269a205e4 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.PasswordMetrics; 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 76aa40c04e3d..2e200a9268f5 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 class MockSystemServices { 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 @@ public class MockSystemServices { 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 class MockSystemServices { } } + 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 55c48e07162b..f0a5f7583661 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 android.util.SparseArray; 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 @@ public class UserRestrictionsUtilsTest { @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 @@ public class UserRestrictionsUtilsTest { @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 @@ public class UserRestrictionsUtilsTest { @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 @@ public class UserRestrictionsUtilsTest { } @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 @@ public class UserRestrictionsUtilsTest { @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 963b27e010fa..bf58443194e5 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.TunerFrontendInfo; 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { } @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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { } @Test - public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() { + public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() throws RemoteException { /**** Register Clients and Set Priority ****/ + 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); - // 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); - - 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 @@ public class TunerResourceManagerServiceTest { // 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { // 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { /**** 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 @@ public class TunerResourceManagerServiceTest { // Predefined second frontend request request = tunerFrontendRequest( - ownerClientId1[0] /*clientId*/, + ownerClient1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); // Second request call @@ -1170,43 +1076,35 @@ public class TunerResourceManagerServiceTest { // 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 @@ public class TunerResourceManagerServiceTest { 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 @@ public class TunerResourceManagerServiceTest { // 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 @@ public class TunerResourceManagerServiceTest { .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 @@ public class TunerResourceManagerServiceTest { 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 f3ecfcc933b4..66788b6d2444 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 android.view.InputDevice; 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 class InputDeviceDelegateTest { 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 @@ public class InputDeviceDelegateTest { 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 index 84f8412fb4b5..f69d1c42eb14 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 org.junit.Test; 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 38cd49daf99d..c7a136ad0f9f 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.FakeSettingsProvider; 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 @@ public class VibrationSettingsTest { if (expectedAllowedVibrations.contains(usage)) { assertVibrationNotIgnoredForUsage(usage); } else { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_BACKGROUND); } } } @@ -350,7 +352,7 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { if (expectedAllowedVibrations.contains(usage)) { assertVibrationNotIgnoredForUsage(usage); } else { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER); + assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_POWER); } } } @@ -426,7 +428,7 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { // 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 @@ public class VibrationSettingsTest { } 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 @@ public class VibrationSettingsTest { 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 @@ public class VibrationSettingsTest { } 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 @@ public class VibrationSettingsTest { 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 bfdaa785a669..31cc50f18299 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 androidx.test.filters.LargeTest; 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 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); + verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED); } @Test @@ -202,7 +204,7 @@ public class VibrationThreadTest { waitForCompletion(); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); - verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); + verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED); } @Test @@ -216,7 +218,7 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { // 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 @@ public class VibrationThreadTest { .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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { .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 3466bbb26fc8..cd057b619000 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 static org.junit.Assert.assertTrue; 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 @@ public class VibratorFrameworkStatsLoggerTest { } 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 4afb56265563..4012575cda88 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.internal.util.test.FakeSettingsProviderRule; 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { } @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 @@ public class VibratorManagerServiceTest { } @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 @@ public class VibratorManagerServiceTest { } @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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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 @@ public class VibratorManagerServiceTest { 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/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index af4394acce67..0c1fbf3cb3d7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -25,11 +25,15 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -37,6 +41,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -46,11 +51,14 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.annotation.Nullable; +import android.graphics.Rect; +import android.gui.DropInputMode; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -833,6 +841,353 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(activity); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation run by the remote handler. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideWithOnlyTaskFragmentFillingTask() { + final Task task = createTask(mDisplayContent); + final ActivityRecord closingActivity = createActivityRecord(task); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + + // Make sure the TaskFragment is not embedded. + assertFalse(taskFragment.isEmbeddedWithBoundsOverride()); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(closingActivity); + prepareActivityForAppTransition(openingActivity); + final int uid = 12345; + closingActivity.info.applicationInfo.uid = uid; + openingActivity.info.applicationInfo.uid = uid; + task.effectiveUid = uid; + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, + null /* changingTaskFragment */); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation is not run by the remote handler because the activity is filling the Task. + assertFalse(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_overrideWithTaskFragmentNotFillingTask() { + final Task task = createTask(mDisplayContent); + final ActivityRecord closingActivity = createActivityRecord(task); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + + // Make sure the TaskFragment is embedded. + taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + final Rect embeddedBounds = new Rect(task.getBounds()); + embeddedBounds.right = embeddedBounds.left + embeddedBounds.width() / 2; + taskFragment.setBounds(embeddedBounds); + assertTrue(taskFragment.isEmbeddedWithBoundsOverride()); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(closingActivity); + prepareActivityForAppTransition(openingActivity); + final int uid = 12345; + closingActivity.info.applicationInfo.uid = uid; + openingActivity.info.applicationInfo.uid = uid; + task.effectiveUid = uid; + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, + null /* changingTaskFragment */); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation run by the remote handler. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Closing non-embedded activity. + final ActivityRecord closingActivity = createActivityRecord(task); + prepareActivityForAppTransition(closingActivity); + // Opening TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(openingActivity); + task.effectiveUid = openingActivity.getUid(); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation run by the remote handler. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Closing TaskFragment with embedded activity. + final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord closingActivity = taskFragment1.getTopMostActivity(); + prepareActivityForAppTransition(closingActivity); + closingActivity.info.applicationInfo.uid = 12345; + // Opening TaskFragment with embedded activity with different UID. + final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment2.getTopMostActivity(); + prepareActivityForAppTransition(openingActivity); + openingActivity.info.applicationInfo.uid = 54321; + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation run by the remote handler. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Closing activity in Task1. + final ActivityRecord closingActivity = createActivityRecord(mDisplayContent); + prepareActivityForAppTransition(closingActivity); + // Opening TaskFragment with embedded activity in Task2. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord openingActivity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(openingActivity); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation not run by the remote handler. + assertFalse(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Closing TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord closingActivity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(closingActivity); + closingActivity.info.applicationInfo.uid = 12345; + task.effectiveUid = closingActivity.getUid(); + // Opening non-embedded activity with different UID. + final ActivityRecord openingActivity = createActivityRecord(task); + prepareActivityForAppTransition(openingActivity); + openingActivity.info.applicationInfo.uid = 54321; + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation should not run by the remote handler when there are non-embedded activities of + // different UID. + assertFalse(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with embedded activity. + final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + prepareActivityForAppTransition(activity); + // Set wallpaper as visible. + final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, + mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); + spyOn(mDisplayContent.mWallpaperController); + doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // Animation should not run by the remote handler when there is wallpaper in the transition. + assertFalse(remoteAnimationRunner.isAnimationStarted()); + } + + @Test + public void testOverrideTaskFragmentAdapter_inputProtectedForUntrustedAnimation() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with embedded activities, one is trusted embedded, and the other + // one is untrusted embedded. + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(2) + .setOrganizer(organizer) + .build(); + final ActivityRecord activity0 = taskFragment.getChildAt(0).asActivityRecord(); + final ActivityRecord activity1 = taskFragment.getChildAt(1).asActivityRecord(); + // Also create a non-embedded activity in the Task. + final ActivityRecord activity2 = new ActivityBuilder(mAtm).build(); + task.addChild(activity2, POSITION_BOTTOM); + prepareActivityForAppTransition(activity0); + prepareActivityForAppTransition(activity1); + prepareActivityForAppTransition(activity2); + doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); + doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // The animation will be animated remotely by client and all activities are input disabled + // for untrusted animation. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + verify(activity0).setDropInputForAnimation(true); + verify(activity1).setDropInputForAnimation(true); + verify(activity2).setDropInputForAnimation(true); + verify(activity0).setDropInputMode(DropInputMode.ALL); + verify(activity1).setDropInputMode(DropInputMode.ALL); + verify(activity2).setDropInputMode(DropInputMode.ALL); + + // Reset input after animation is finished. + clearInvocations(activity0); + clearInvocations(activity1); + clearInvocations(activity2); + remoteAnimationRunner.finishAnimation(); + + verify(activity0).setDropInputForAnimation(false); + verify(activity1).setDropInputForAnimation(false); + verify(activity2).setDropInputForAnimation(false); + verify(activity0).setDropInputMode(DropInputMode.OBSCURED); + verify(activity1).setDropInputMode(DropInputMode.NONE); + verify(activity2).setDropInputMode(DropInputMode.NONE); + } + + /** + * Since we don't have any use case to rely on handling input during animation, disable it even + * if it is trusted embedding so that it could cover some edge-cases when a previously trusted + * host starts doing something bad. + */ + @Test + public void testOverrideTaskFragmentAdapter_inputProtectedForTrustedAnimation() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with only trusted embedded activity + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setOrganizer(organizer) + .build(); + final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord(); + prepareActivityForAppTransition(activity); + doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // The animation will be animated remotely by client and all activities are input disabled + // for untrusted animation. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + verify(activity).setDropInputForAnimation(true); + verify(activity).setDropInputMode(DropInputMode.ALL); + + // Reset input after animation is finished. + clearInvocations(activity); + remoteAnimationRunner.finishAnimation(); + + verify(activity).setDropInputForAnimation(false); + verify(activity).setDropInputMode(DropInputMode.NONE); + } + + /** + * We don't need to drop input for fully trusted embedding (system app, and embedding in the + * same app). This will allow users to do fast tapping. + */ + @Test + public void testOverrideTaskFragmentAdapter_noInputProtectedForFullyTrustedAnimation() { + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner(); + setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner); + + // Create a TaskFragment with only trusted embedded activity + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setOrganizer(organizer) + .build(); + final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord(); + prepareActivityForAppTransition(activity); + final int uid = mAtm.mTaskFragmentOrganizerController.getTaskFragmentOrganizerUid( + getITaskFragmentOrganizer(organizer)); + doReturn(true).when(task).isFullyTrustedEmbedding(uid); + spyOn(mDisplayContent.mAppTransition); + + // Prepare and start transition. + prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + + // The animation will be animated remotely by client, but input should not be dropped for + // fully trusted. + assertTrue(remoteAnimationRunner.isAnimationStarted()); + verify(activity, never()).setDropInputForAnimation(true); + verify(activity, never()).setDropInputMode(DropInputMode.ALL); + } + + @Test public void testTransitionGoodToGoForTaskFragments() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); final Task task = createTask(mDisplayContent); @@ -898,6 +1253,22 @@ public class AppTransitionControllerTest extends WindowTestsBase { verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any()); } + /** Registers remote animation for the organizer. */ + private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer, + TestRemoteAnimationRunner remoteAnimationRunner) { + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + remoteAnimationRunner, 10, 1); + final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer); + final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter); + definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter); + registerTaskFragmentOrganizer(iOrganizer); + mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition); + } + private static ITaskFragmentOrganizer getITaskFragmentOrganizer( TaskFragmentOrganizer organizer) { return ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java index affaad6d9210..193390833789 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; @@ -316,6 +317,50 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase { verify(mScreenUnblocker).sendToTarget(); } + @Test + public void testWaitForAllWindowsDrawnForInvalidDisplay_usesTransitionToUnblock() { + mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH); + + final WindowState defaultDisplayWindow = createWindow(/* parent= */ null, + TYPE_BASE_APPLICATION, mDisplayContent, "DefaultDisplayWindow"); + makeWindowVisibleAndNotDrawn(defaultDisplayWindow); + + mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true); + + mWmInternal.waitForAllWindowsDrawn(mScreenUnblocker, + /* timeout= */ Integer.MAX_VALUE, INVALID_DISPLAY); + + // Perform display update + mUniqueId = "new_default_display_unique_id"; + mDisplayContent.requestDisplayUpdate(mock(Runnable.class)); + + when(mDisplayContent.mTransitionController.inTransition()).thenReturn(true); + + // Notify that transition started collecting + captureStartTransitionCollection().getAllValues().forEach((callback) -> + callback.onCollectStarted(/* deferred= */ true)); + + // Verify that screen is not unblocked yet + verify(mScreenUnblocker, never()).sendToTarget(); + + // Make all display windows drawn + defaultDisplayWindow.mWinAnimator.mDrawState = HAS_DRAWN; + mWm.mRoot.performSurfacePlacement(); + + // Verify that default display is still not unblocked yet + // (so it doesn't use old windows drawn path) + verify(mScreenUnblocker, never()).sendToTarget(); + + // Mark start transaction as presented + when(mDisplayContent.mTransitionController.inTransition()).thenReturn(false); + captureRequestedTransition().getAllValues().forEach( + this::makeTransitionTransactionCompleted); + + // Verify that the default screen unblocker is sent only after start transaction + // of the Shell transition is presented + verify(mScreenUnblocker).sendToTarget(); + } + private void prepareSecondaryDisplay() { mSecondaryDisplayContent = createNewDisplay(); when(mSecondaryScreenUnblocker.getTarget()).thenReturn(mWm.mH); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index d013053a063d..a71b81e025d2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -90,6 +90,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.view.RemoteAnimationDefinition; import android.view.SurfaceControl; import android.window.IRemoteTransition; import android.window.ITaskFragmentOrganizer; @@ -139,6 +140,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { private IBinder mFragmentToken; private WindowContainerTransaction mTransaction; private WindowContainerToken mFragmentWindowToken; + private RemoteAnimationDefinition mDefinition; private IBinder mErrorToken; private Rect mTaskFragBounds; @@ -167,6 +169,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mTransaction = new WindowContainerTransaction(); mTransaction.setTaskFragmentOrganizer(mIOrganizer); mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); + mDefinition = new RemoteAnimationDefinition(); mErrorToken = new Binder(); final Rect displayBounds = mDisplayContent.getBounds(); mTaskFragBounds = new Rect(displayBounds.left, displayBounds.top, displayBounds.centerX(), @@ -576,6 +579,17 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testRegisterRemoteAnimations() { + mController.registerRemoteAnimations(mIOrganizer, mDefinition); + + assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer)); + + mController.unregisterRemoteAnimations(mIOrganizer); + + assertNull(mController.getRemoteAnimationDefinition(mIOrganizer)); + } + + @Test public void testApplyTransaction_disallowRemoteTransitionForNonSystemOrganizer() { mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java index 808a57589b47..1e1abf283af8 100644 --- a/telecomm/java/android/telecom/CallControl.java +++ b/telecomm/java/android/telecom/CallControl.java @@ -39,7 +39,10 @@ import java.util.concurrent.Executor; /** * 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 1df6cf78047c..ad7d9870ca98 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -2621,7 +2621,9 @@ public abstract class Connection extends Conferenceable { } /** - * 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 @@ public abstract class Connection extends Conferenceable { } /** - * 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 cba2eeae5c9f..13bd5eb67e44 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -10014,6 +10014,19 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 7481daa8e396..bc709abbe76e 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -132,6 +132,7 @@ import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.flags.Flags; +import com.android.internal.telephony.util.TelephonyUtils; import com.android.telephony.Rlog; import java.io.IOException; @@ -9975,6 +9976,7 @@ public class TelephonyManager { ALLOWED_NETWORK_TYPES_REASON_POWER, ALLOWED_NETWORK_TYPES_REASON_CARRIER, ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, + ALLOWED_NETWORK_TYPES_REASON_TEST, }) @Retention(RetentionPolicy.SOURCE) public @interface AllowedNetworkTypesReason { @@ -10013,6 +10015,14 @@ public class TelephonyManager { public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; /** + * To indicate allowed network type change is requested by Testing purpose, should be default to + * {@link #getAllNetworkTypesBitmask} when done testing. + * + * @hide + */ + public static final int ALLOWED_NETWORK_TYPES_REASON_TEST = 4; + + /** * Set the allowed network types of the device and provide the reason triggering the allowed * network change. * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or @@ -10118,6 +10128,8 @@ public class TelephonyManager { case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER: case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G: return true; + case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_TEST: + return TelephonyUtils.IS_DEBUGGABLE; } return false; } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index c5934a70610f..83fc0dcfe790 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -592,7 +592,7 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(), + telephony.requestSatelliteEnabled(attributes.isEnabled(), attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback); } else { Rlog.e(TAG, "requestEnabled() invalid telephony"); @@ -650,7 +650,7 @@ public final class SatelliteManager { } } }; - telephony.requestIsSatelliteEnabled(mSubId, receiver); + telephony.requestIsSatelliteEnabled(receiver); } else { loge("requestIsEnabled() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -707,7 +707,7 @@ public final class SatelliteManager { } } }; - telephony.requestIsDemoModeEnabled(mSubId, receiver); + telephony.requestIsDemoModeEnabled(receiver); } else { loge("requestIsDemoModeEnabled() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -764,7 +764,7 @@ public final class SatelliteManager { } } }; - telephony.requestIsEmergencyModeEnabled(mSubId, receiver); + telephony.requestIsEmergencyModeEnabled(receiver); } else { executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); @@ -821,7 +821,7 @@ public final class SatelliteManager { } } }; - telephony.requestIsSatelliteSupported(mSubId, receiver); + telephony.requestIsSatelliteSupported(receiver); } else { loge("requestIsSupported() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -878,7 +878,7 @@ public final class SatelliteManager { } } }; - telephony.requestSatelliteCapabilities(mSubId, receiver); + telephony.requestSatelliteCapabilities(receiver); } else { loge("requestCapabilities() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -1204,8 +1204,7 @@ public final class SatelliteManager { } }; sSatelliteTransmissionUpdateCallbackMap.put(callback, internalCallback); - telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback, - internalCallback); + telephony.startSatelliteTransmissionUpdates(errorCallback, internalCallback); } else { loge("startTransmissionUpdates() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( @@ -1255,8 +1254,7 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.stopSatelliteTransmissionUpdates(mSubId, errorCallback, - internalCallback); + telephony.stopSatelliteTransmissionUpdates(errorCallback, internalCallback); // TODO: Notify SmsHandler that pointing UI stopped } else { loge("stopSatelliteTransmissionUpdates: No internal callback."); @@ -1312,7 +1310,7 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData, + cancelRemote = telephony.provisionSatelliteService(token, provisionData, errorCallback); } else { loge("provisionService() invalid telephony"); @@ -1364,7 +1362,7 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.deprovisionSatelliteService(mSubId, token, errorCallback); + telephony.deprovisionSatelliteService(token, errorCallback); } else { loge("deprovisionService() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( @@ -1419,8 +1417,7 @@ public final class SatelliteManager { } }; sSatelliteProvisionStateCallbackMap.put(callback, internalCallback); - return telephony.registerForSatelliteProvisionStateChanged( - mSubId, internalCallback); + return telephony.registerForSatelliteProvisionStateChanged(internalCallback); } else { throw new IllegalStateException("telephony service is null."); } @@ -1453,7 +1450,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForSatelliteProvisionStateChanged(mSubId, internalCallback); + telephony.unregisterForSatelliteProvisionStateChanged(internalCallback); } else { loge("unregisterForProvisionStateChanged: No internal callback."); } @@ -1510,7 +1507,7 @@ public final class SatelliteManager { } } }; - telephony.requestIsSatelliteProvisioned(mSubId, receiver); + telephony.requestIsSatelliteProvisioned(receiver); } else { loge("requestIsProvisioned() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -1560,7 +1557,7 @@ public final class SatelliteManager { } }; sSatelliteModemStateCallbackMap.put(callback, internalCallback); - return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback); + return telephony.registerForSatelliteModemStateChanged(internalCallback); } else { throw new IllegalStateException("telephony service is null."); } @@ -1593,7 +1590,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForModemStateChanged(mSubId, internalCallback); + telephony.unregisterForModemStateChanged(internalCallback); } else { loge("unregisterForModemStateChanged: No internal callback."); } @@ -1656,7 +1653,7 @@ public final class SatelliteManager { } }; sSatelliteDatagramCallbackMap.put(callback, internalCallback); - return telephony.registerForIncomingDatagram(mSubId, internalCallback); + return telephony.registerForIncomingDatagram(internalCallback); } else { throw new IllegalStateException("telephony service is null."); } @@ -1688,7 +1685,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForIncomingDatagram(mSubId, internalCallback); + telephony.unregisterForIncomingDatagram(internalCallback); } else { loge("unregisterForIncomingDatagram: No internal callback."); } @@ -1732,7 +1729,7 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.pollPendingDatagrams(mSubId, internalCallback); + telephony.pollPendingDatagrams(internalCallback); } else { loge("pollPendingDatagrams() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity( @@ -1790,7 +1787,7 @@ public final class SatelliteManager { () -> resultListener.accept(result))); } }; - telephony.sendDatagram(mSubId, datagramType, datagram, + telephony.sendDatagram(datagramType, datagram, needFullScreenPointingUI, internalCallback); } else { loge("sendDatagram() invalid telephony"); @@ -1908,7 +1905,7 @@ public final class SatelliteManager { } } }; - telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver); + telephony.requestTimeForNextSatelliteVisibility(receiver); } else { loge("requestTimeForNextSatelliteVisibility() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -1939,7 +1936,7 @@ public final class SatelliteManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { - telephony.setDeviceAlignedWithSatellite(mSubId, isAligned); + telephony.setDeviceAlignedWithSatellite(isAligned); } else { throw new IllegalStateException("telephony service is null."); } @@ -2203,7 +2200,7 @@ public final class SatelliteManager { } } }; - telephony.requestNtnSignalStrength(mSubId, receiver); + telephony.requestNtnSignalStrength(receiver); } else { loge("requestNtnSignalStrength() invalid telephony"); executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( @@ -2254,7 +2251,7 @@ public final class SatelliteManager { ntnSignalStrength))); } }; - telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback); + telephony.registerForNtnSignalStrengthChanged(internalCallback); sNtnSignalStrengthCallbackMap.put(callback, internalCallback); } else { throw new IllegalStateException("Telephony service is null."); @@ -2294,7 +2291,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback); + telephony.unregisterForNtnSignalStrengthChanged(internalCallback); } else { loge("unregisterForNtnSignalStrengthChanged: No internal callback."); throw new IllegalArgumentException("callback is not valid"); @@ -2339,7 +2336,7 @@ public final class SatelliteManager { } }; sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback); - return telephony.registerForCapabilitiesChanged(mSubId, internalCallback); + return telephony.registerForCapabilitiesChanged(internalCallback); } else { throw new IllegalStateException("Telephony service is null."); } @@ -2372,7 +2369,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForCapabilitiesChanged(mSubId, internalCallback); + telephony.unregisterForCapabilitiesChanged(internalCallback); } else { loge("unregisterForCapabilitiesChanged: No internal callback."); } @@ -2448,7 +2445,7 @@ public final class SatelliteManager { }; sSatelliteSupportedStateCallbackMap.put(callback, internalCallback); return telephony.registerForSatelliteSupportedStateChanged( - mSubId, internalCallback); + internalCallback); } else { throw new IllegalStateException("telephony service is null."); } @@ -2483,7 +2480,7 @@ public final class SatelliteManager { ITelephony telephony = getITelephony(); if (telephony != null) { if (internalCallback != null) { - telephony.unregisterForSatelliteSupportedStateChanged(mSubId, internalCallback); + telephony.unregisterForSatelliteSupportedStateChanged(internalCallback); } else { loge("unregisterForSupportedStateChanged: No internal callback."); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 0c5f30f96360..e852e6bbb756 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2743,7 +2743,6 @@ interface ITelephony { /** * Request to enable or disable the satellite modem. * - * @param subId The subId of the subscription to enable or disable the satellite modem for. * @param enableSatellite True to enable the satellite modem and false to disable. * @param enableDemoMode True if demo mode is enabled and false otherwise. When * disabling satellite, {@code enableDemoMode} is always considered as @@ -2755,93 +2754,83 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode, + void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode, boolean isEmergency, in IIntegerConsumer callback); /** * Request to get whether the satellite modem is enabled. * - * @param subId The subId of the subscription to request whether satellite is enabled for. * @param receiver Result receiver to get the error code of the request and whether the * satellite modem is enabled. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestIsSatelliteEnabled(int subId, in ResultReceiver receiver); + void requestIsSatelliteEnabled(in ResultReceiver receiver); /** * Request to get whether the satellite service demo mode is enabled. * - * @param subId The subId of the subscription to request whether the satellite demo mode is - * enabled for. * @param receiver Result receiver to get the error code of the request and whether the * satellite demo mode is enabled. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestIsDemoModeEnabled(int subId, in ResultReceiver receiver); + void requestIsDemoModeEnabled(in ResultReceiver receiver); /** * Request to get whether the satellite service is enabled with emergency mode. * - * @param subId The subId of the subscription to request whether the satellite demo mode is - * enabled for. * @param receiver Result receiver to get the error code of the request and whether the * satellite is enabled with emergency mode. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestIsEmergencyModeEnabled(int subId, in ResultReceiver receiver); + void requestIsEmergencyModeEnabled(in ResultReceiver receiver); /** * Request to get whether the satellite service is supported on the device. * - * @param subId The subId of the subscription to check whether satellite is supported for. * @param receiver Result receiver to get the error code of the request and whether the * satellite service is supported on the device. */ - void requestIsSatelliteSupported(int subId, in ResultReceiver receiver); + void requestIsSatelliteSupported(in ResultReceiver receiver); /** * Request to get the capabilities of the satellite service. * - * @param subId The subId of the subscription to get the capabilities for. * @param receiver Result receiver to get the error code of the request and the requested * capabilities of the satellite service. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestSatelliteCapabilities(int subId, in ResultReceiver receiver); + void requestSatelliteCapabilities(in ResultReceiver receiver); /** * Start receiving satellite transmission updates. * - * @param subId The subId of the subscription to stop satellite transmission updates for. * @param resultCallback The callback to get the result of the request. * @param callback The callback to handle transmission updates. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void startSatelliteTransmissionUpdates(int subId, in IIntegerConsumer resultCallback, + void startSatelliteTransmissionUpdates(in IIntegerConsumer resultCallback, in ISatelliteTransmissionUpdateCallback callback); /** * Stop receiving satellite transmission updates. * - * @param subId The subId of the subscritpion to stop satellite transmission updates for. * @param resultCallback The callback to get the result of the request. * @param callback The callback that was passed to startSatelliteTransmissionUpdates. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void stopSatelliteTransmissionUpdates(int subId, in IIntegerConsumer resultCallback, + void stopSatelliteTransmissionUpdates(in IIntegerConsumer resultCallback, in ISatelliteTransmissionUpdateCallback callback); /** * Register the subscription with a satellite provider. * This is needed to register the subscription if the provider allows dynamic registration. * - * @param subId The subId of the subscription to be provisioned. * @param token The token to be used as a unique identifier for provisioning with satellite * gateway. * @provisionData Data from the provisioning app that can be used by provisioning server @@ -2851,7 +2840,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - ICancellationSignal provisionSatelliteService(int subId, in String token, + ICancellationSignal provisionSatelliteService(in String token, in byte[] provisionData, in IIntegerConsumer callback); /** @@ -2861,110 +2850,99 @@ interface ITelephony { * {@link SatelliteCallback.SatelliteProvisionStateListener#onSatelliteProvisionStateChanged} * should report as deprovisioned. * - * @param subId The subId of the subscription to be deprovisioned. * @param token The token of the device/subscription to be deprovisioned. * @param callback The callback to get the result of the request. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void deprovisionSatelliteService(int subId, in String token, in IIntegerConsumer callback); + void deprovisionSatelliteService(in String token, in IIntegerConsumer callback); /** * Registers for provision state changed from satellite modem. * - * @param subId The subId of the subscription to register for provision state changed. * @param callback The callback to handle the satellite provision state changed event. * * @return The {@link SatelliteError} result of the operation. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteProvisionStateChanged(int subId, - in ISatelliteProvisionStateCallback callback); + int registerForSatelliteProvisionStateChanged(in ISatelliteProvisionStateCallback callback); /** * Unregisters for provision state changed from satellite modem. * If callback was not registered before, the request will be ignored. * - * @param subId The subId of the subscription to unregister for provision state changed. * @param callback The callback that was passed to registerForSatelliteProvisionStateChanged. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteProvisionStateChanged(int subId, + void unregisterForSatelliteProvisionStateChanged( in ISatelliteProvisionStateCallback callback); /** * Request to get whether the device is provisioned with a satellite provider. * - * @param subId The subId of the subscription to get whether the device is provisioned for. * @param receiver Result receiver to get the error code of the request and whether the * device is provisioned with a satellite provider. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestIsSatelliteProvisioned(int subId, in ResultReceiver receiver); + void requestIsSatelliteProvisioned(in ResultReceiver receiver); /** * Registers for modem state changed from satellite modem. * - * @param subId The subId of the subscription to register for satellite modem state changed. * @param callback The callback to handle the satellite modem state changed event. * * @return The {@link SatelliteError} result of the operation. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback); + int registerForSatelliteModemStateChanged(ISatelliteModemStateCallback callback); /** * Unregisters for modem state changed from satellite modem. * If callback was not registered before, the request will be ignored. * - * @param subId The subId of the subscription to unregister for satellite modem state changed. * @param callback The callback that was passed to registerForSatelliteStateChanged. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForModemStateChanged(int subId, ISatelliteModemStateCallback callback); + void unregisterForModemStateChanged(ISatelliteModemStateCallback callback); /** * Register to receive incoming datagrams over satellite. * - * @param subId The subId of the subscription to register for incoming satellite datagrams. * @param callback The callback to handle the incoming datagrams. * * @return The {@link SatelliteError} result of the operation. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForIncomingDatagram(int subId, ISatelliteDatagramCallback callback); + int registerForIncomingDatagram(ISatelliteDatagramCallback callback); /** * Unregister to stop receiving incoming datagrams over satellite. * If callback was not registered before, the request will be ignored. * - * @param subId The subId of the subscription to unregister for incoming satellite datagrams. * @param callback The callback that was passed to registerForIncomingDatagram. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForIncomingDatagram(int subId, ISatelliteDatagramCallback callback); + void unregisterForIncomingDatagram(ISatelliteDatagramCallback callback); /** * Poll pending satellite datagrams over satellite. * - * @param subId The subId of the subscription used for receiving datagrams. * @param callback The callback to get the result of the request. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void pollPendingDatagrams(int subId, IIntegerConsumer callback); + void pollPendingDatagrams(IIntegerConsumer callback); /** * Send datagram over satellite. * - * @param subId The subId of the subscription to send satellite datagrams for. * @param datagramType Type of datagram. * @param datagram Datagram to send over satellite. * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in @@ -2973,7 +2951,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void sendDatagram(int subId, int datagramType, in SatelliteDatagram datagram, + void sendDatagram(int datagramType, in SatelliteDatagram datagram, in boolean needFullScreenPointingUI, IIntegerConsumer callback); /** @@ -2991,13 +2969,12 @@ interface ITelephony { /** * Request to get the time after which the satellite will be visible. * - * @param subId The subId to get the time after which the satellite will be visible for. * @param receiver Result receiver to get the error code of the request and the requested * time after which the satellite will be visible. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestTimeForNextSatelliteVisibility(int subId, in ResultReceiver receiver); + void requestTimeForNextSatelliteVisibility(in ResultReceiver receiver); /** * Inform whether the device is aligned with the satellite within in margin for demo mode. @@ -3007,7 +2984,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void setDeviceAlignedWithSatellite(int subId, boolean isAligned); + void setDeviceAlignedWithSatellite(boolean isAligned); /** * This API can be used by only CTS to update satellite vendor service package name. @@ -3163,20 +3140,18 @@ interface ITelephony { /** * Request to get the signal strength of the satellite connection. * - * @param subId The subId of the subscription to request for. * @param receiver Result receiver to get the error code of the request and the current signal * strength of the satellite connection. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void requestNtnSignalStrength(int subId, in ResultReceiver receiver); + void requestNtnSignalStrength(in ResultReceiver receiver); /** * Registers for NTN signal strength changed from satellite modem. If the registration operation * is not successful, a {@link SatelliteException} that contains {@link SatelliteResult} will be * thrown. * - * @param subId The subId of the subscription to request for. * @param callback The callback to handle the NTN signal strength changed event. If the * operation is successful, {@link NtnSignalStrengthCallback#onNtnSignalStrengthChanged( * NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of @@ -3185,30 +3160,27 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void registerForNtnSignalStrengthChanged(int subId, - in INtnSignalStrengthCallback callback); + void registerForNtnSignalStrengthChanged(in INtnSignalStrengthCallback callback); /** * Unregisters for NTN signal strength changed from satellite modem. * If callback was not registered before, the request will be ignored. * - * @param subId The subId of the subscription to unregister for provision state changed. * @param callback The callback that was passed to * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback); + void unregisterForNtnSignalStrengthChanged(in INtnSignalStrengthCallback callback); /** * Registers for satellite capabilities change event from the satellite service. * - * @param executor The executor on which the callback will be called. * @param callback The callback to handle the satellite capabilities changed event. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForCapabilitiesChanged(int subId, in ISatelliteCapabilitiesCallback callback); + int registerForCapabilitiesChanged(in ISatelliteCapabilitiesCallback callback); /** * Unregisters for satellite capabilities change event from the satellite service. @@ -3219,8 +3191,7 @@ interface ITelephony { */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForCapabilitiesChanged(int subId, - in ISatelliteCapabilitiesCallback callback); + void unregisterForCapabilitiesChanged(in ISatelliteCapabilitiesCallback callback); /** * This API can be used by only CTS to override the cached value for the device overlay config @@ -3329,26 +3300,24 @@ interface ITelephony { /** * Registers for supported state changed from satellite modem. * - * @param subId The subId of the subscription to register for supported state changed. * @param callback The callback to handle the satellite supported state changed event. * * @return The {@link SatelliteError} result of the operation. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - int registerForSatelliteSupportedStateChanged(int subId, + int registerForSatelliteSupportedStateChanged( in ISatelliteSupportedStateCallback callback); /** * Unregisters for supported state changed from satellite modem. * If callback was not registered before, the request will be ignored. * - * @param subId The subId of the subscription to unregister for supported state changed. * @param callback The callback that was passed to registerForSatelliteSupportedStateChanged. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") - void unregisterForSatelliteSupportedStateChanged(int subId, + void unregisterForSatelliteSupportedStateChanged( in ISatelliteSupportedStateCallback callback); /** diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png Binary files differindex b2354f8f4799..297244f9d6d1 100644 --- a/tests/Input/assets/testPointerFillStyle.png +++ b/tests/Input/assets/testPointerFillStyle.png diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh index d97dd7c18045..5c5421a9151a 100755 --- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh +++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh @@ -213,9 +213,9 @@ com.unsupported.* " run_hoststubgen_for_failure "One specific class disallowed" \ - "TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \ + "TinyFrameworkAnnotations is not allowed to have Ravenwood annotations" \ " -!com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +!com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations * # All other classes allowed " diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt index 6cf214300b43..7dd4fdd078a2 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt @@ -202,18 +202,6 @@ fun isAnonymousInnerClass(cn: ClassNode): Boolean { } /** - * Take a class name. If it's a nested class, then return the name of its direct outer class name. - * Otherwise, return null. - */ -fun getDirectOuterClassName(className: String): String? { - val pos = className.lastIndexOf('$') - if (pos < 0) { - return null - } - return className.substring(0, pos) -} - -/** * Write bytecode to push all the method arguments to the stack. * The number of arguments and their type are taken from [methodDescriptor]. */ diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt index 47790b10782a..37048d9c7c60 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt @@ -16,7 +16,6 @@ package com.android.hoststubgen.filters import com.android.hoststubgen.asm.ClassNodes -import com.android.hoststubgen.asm.getDirectOuterClassName /** * This is used as the second last fallback filter. This filter propagates the class-wide policy @@ -24,72 +23,69 @@ import com.android.hoststubgen.asm.getDirectOuterClassName */ class ClassWidePolicyPropagatingFilter( private val classes: ClassNodes, - fallback: OutputFilter, - ) : DelegatingFilter(fallback) { + fallback: OutputFilter +) : DelegatingFilter(fallback) { - private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? { + /** + * We don't use ClassNode.outerClass, because it gives as the top-level + * outer class (A$B$C -> A), not the direct outer class (A$B$C -> A$B). + * + * Sometimes a class name includes `$`, but is not as a nested class name separator + * (e.g. a class name like `MyClass$$`). In this case, `MyClass$` is not actually a class. + * + * So before getting the class policy on a nonexistent class, which may cause an + * incorrect result, we make sure the class actually exists. + */ + private fun getDirectOuterClass(className: String): String? { var currentClass = className - - - // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy - // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A. while (true) { - // Sometimes a class name has a `$` in it but not as a nest class name separator -- - // e.g. class name like "MyClass$$". In this case, `MyClass$` may not actually be - // a class name. - // So before getting the class policy on a nonexistent class, which may cause an - // incorrect result, we make sure if className actually exists. - if (classes.hasClass(className)) { - outermostFilter.getPolicyForClass(className).let { policy -> - if (policy.policy.isClassWidePolicy) { - val p = if (resolve) { - policy.policy.resolveClassWidePolicy() - } else { - policy.policy - } - - return p.withReason(policy.reason) - .wrapReason("class-wide in $currentClass") - } - // If the class's policy is remove, then remove it. - if (policy.policy == FilterPolicy.Remove) { - return FilterPolicy.Remove.withReason("class-wide in $currentClass") - } - } - } - - // Next, look at the outer class... - val outer = getDirectOuterClassName(currentClass) - if (outer == null) { + val pos = currentClass.lastIndexOf('$') + if (pos < 0) { return null } - currentClass = outer + currentClass = currentClass.substring(0, pos) + if (classes.hasClass(currentClass)) { + return currentClass + } } } - override fun getPolicyForClass(className: String): FilterPolicyWithReason { - // If it's a nested class, use the outer class's policy. - getDirectOuterClassName(className)?.let { outerName -> - getClassWidePolicy(outerName, resolve = false)?.let { policy -> - return policy + private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? { + outermostFilter.getPolicyForClass(className).let { policy -> + if (policy.policy.isClassWidePolicy) { + val p = if (resolve) { + policy.policy.resolveClassWidePolicy() + } else { + policy.policy + } + + return p.withReason(policy.reason) + .wrapReason("class-wide in $className") + } + // If the class's policy is remove, then remove it. + if (policy.policy == FilterPolicy.Remove) { + return FilterPolicy.Remove.withReason("class-wide in $className") } } + return null + } - return super.getPolicyForClass(className) + override fun getPolicyForClass(className: String): FilterPolicyWithReason { + // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy + // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A, recursively + return getDirectOuterClass(className)?.let { getClassWidePolicy(it, resolve = false) } + ?: super.getPolicyForClass(className) } - override fun getPolicyForField( - className: String, - fieldName: String - ): FilterPolicyWithReason { + override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason { return getClassWidePolicy(className, resolve = true) ?: super.getPolicyForField(className, fieldName) } override fun getPolicyForMethod( - className: String, - methodName: String, - descriptor: String + className: String, + methodName: String, + descriptor: String ): FilterPolicyWithReason { return getClassWidePolicy(className, resolve = true) ?: super.getPolicyForMethod(className, methodName, descriptor) diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt index bd9e85e17890..de4cb0c536c1 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt @@ -6,10 +6,10 @@ # To allow a specific class to use annotations: -# com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +# com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations # To disallow a specific class to use annotations: -# !com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +# !com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations # To allow a specific package to use annotations: # com.android.hoststubgen.test.* diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt index c2f593cf5ae3..845e1d08ce92 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -394,111 +394,13 @@ NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested InnerClasses: public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 3 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - - public static int getOneKeep(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestKeep - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -SourceFile: "TinyFrameworkCallerCheck.java" -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 4 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - x: ireturn - LineNumberTable: - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - x: ireturn - LineNumberTable: -} -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 3, methods: 10, attributes: 2 public int stub; @@ -519,7 +421,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota descriptor: I flags: (0x0001) ACC_PUBLIC - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -536,7 +438,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub @@ -553,7 +455,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 6 1 value I RuntimeInvisibleAnnotations: x: #x() @@ -571,7 +473,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 4 1 value I RuntimeInvisibleAnnotations: x: #x() @@ -589,7 +491,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 8 1 foo Ljava/lang/String; RuntimeInvisibleAnnotations: x: #x() @@ -608,7 +510,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 10 1 value I RuntimeInvisibleAnnotations: x: #x() @@ -630,7 +532,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 4 1 value I public static native int nativeAddThree(int); @@ -668,7 +570,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestThrow @@ -684,12 +586,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub @@ -697,183 +599,104 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 10, attributes: 2 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 3, attributes: 3 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: - stack=2, locals=1, args_size=1 + stack=1, locals=1, args_size=1 x: aload_0 x: invokespecial #x // Method java/lang/Object."<init>":()V - x: aload_0 - x: iconst_1 - x: putfield #x // Field stub:I - x: aload_0 - x: iconst_2 - x: putfield #x // Field keep:I - x: return + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: aload_0 - x: iload_1 - x: invokevirtual #x // Method addOneInner:(I)I + stack=1, locals=0, args_size=0 + x: iconst_1 x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 6 1 value I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep - public int addOneInner(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 + stack=1, locals=0, args_size=0 x: iconst_1 - x: iadd x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I - - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC - Code: - stack=2, locals=2, args_size=2 - x: new #x // class java/lang/RuntimeException - x: dup - x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 8 1 foo Ljava/lang/String; - - public int addTwo(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC - Code: - stack=3, locals=2, args_size=2 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String not supported on host side - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 10 1 value I RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestStub - x: #x(#x=s#x) - android.hosttest.annotation.HostSideTestSubstitute( - suffix="_host" - ) - - public int addTwo_host(int); - descriptor: (I)I +} +SourceFile: "TinyFrameworkCallerCheck.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 - x: iconst_2 - x: iadd - x: ireturn + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I - - public static native int nativeAddThree(int); - descriptor: (I)I - flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub - x: #x(#x=s#x) - android.hosttest.annotation.HostSideTestSubstitute( - suffix="_host" - ) + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - public static int nativeAddThree_host(int); - descriptor: (I)I + public static int getOne_withCheck(); + descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=1, args_size=1 - x: iload_0 - x: iconst_3 - x: iadd + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 value I - - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: ldc #x // String This value shouldn\'t be seen on the host side. - x: areturn - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - x: areturn + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +SourceFile: "TinyFrameworkCallerCheck.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook @@ -936,6 +759,118 @@ SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 6, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String not supported on host side + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 10 1 value I + RuntimeInvisibleAnnotations: + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + public int addTwo_host(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: ldc #x // String This value shouldn\'t be seen on the host side. + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class Compiled from "TinyFrameworkClassWithInitializerDefault.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault @@ -2684,7 +2619,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 4 + interfaces: 0, fields: 2, methods: 1, attributes: 3 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2717,9 +2652,6 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass <no name> final mandated } SourceFile: "TinyFrameworkNestedClasses.java" -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses InnerClasses: public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses @@ -2778,6 +2710,40 @@ NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedCl InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 3 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 8 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass; +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass @@ -2786,7 +2752,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 4 + interfaces: 0, fields: 1, methods: 2, attributes: 3 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2820,13 +2786,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; } SourceFile: "TinyFrameworkNestedClasses.java" -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass @@ -2942,6 +2906,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 @@ -2957,6 +2922,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class Compiled from "TinyFrameworkPackageRedirect.java" diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt index 1b83d244c55c..86a9c65f59b4 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt @@ -216,136 +216,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 2, attributes: 4 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 5 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 1, methods: 5, attributes: 3 public int stub; @@ -360,7 +237,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -449,7 +326,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -462,45 +339,18 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 8, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -514,11 +364,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -529,12 +379,33 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int addOneInner(int); - descriptor: (I)I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -546,11 +417,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -562,11 +433,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addTwo(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -577,10 +448,42 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -594,9 +497,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -604,17 +507,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow + Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -626,7 +530,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -635,28 +539,27 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class - Compiled from "TinyFrameworkClassLoadHook.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 3, attributes: 3 - public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; - descriptor: Ljava/util/Set; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + interfaces: 0, fields: 1, methods: 4, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); descriptor: ()V - flags: (0x0002) ACC_PRIVATE + flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -670,28 +573,43 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static void onClassLoaded(java.lang.Class<?>); - descriptor: (Ljava/lang/Class;)V - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=2, args_size=2 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow - Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - static {}; - descriptor: ()V - flags: (0x0008) ACC_STATIC + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=0, args_size=0 + stack=3, locals=2, args_size=2 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -703,7 +621,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassLoadHook.java" +SourceFile: "TinyFrameworkClassWideAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -2153,7 +2071,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 5 + interfaces: 0, fields: 2, methods: 1, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2199,9 +2117,50 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2211,7 +2170,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 5 + interfaces: 0, fields: 1, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2257,15 +2216,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2406,6 +2363,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -2420,6 +2378,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt index d23b450c4076..c6b9c7a9e4f1 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt @@ -450,155 +450,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 4 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneKeep(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneKeep - x: ldc #x // String ()I - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestKeep - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 5 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 2, methods: 8, attributes: 3 public int stub; @@ -628,12 +486,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V x: return - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -650,7 +508,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -672,7 +530,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 6 1 value I RuntimeVisibleAnnotations: x: #x() @@ -688,7 +546,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOneInner x: ldc #x // String (I)I x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -701,7 +559,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 15 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 15 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -722,7 +580,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 0 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -758,7 +616,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String unsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -790,7 +648,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -800,7 +658,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -813,205 +671,138 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 8, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 3, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: - stack=2, locals=1, args_size=1 + stack=1, locals=1, args_size=1 x: aload_0 x: invokespecial #x // Method java/lang/Object."<init>":()V - x: aload_0 - x: iconst_1 - x: putfield #x // Field stub:I - x: aload_0 - x: iconst_2 - x: putfield #x // Field keep:I - x: return + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: aload_0 - x: iload_1 - x: invokevirtual #x // Method addOneInner:(I)I - x: ireturn + stack=4, locals=0, args_size=0 + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_1 + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 6 1 value I RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep - public int addOneInner(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 + stack=1, locals=0, args_size=0 x: iconst_1 - x: iadd x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC - Code: - stack=2, locals=2, args_size=2 - x: new #x // class java/lang/RuntimeException - x: dup - x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 8 1 foo Ljava/lang/String; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + RuntimeInvisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int addTwo(int); - descriptor: (I)I + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=2, locals=2, args_size=2 - x: iload_1 - x: iconst_2 - x: iadd - x: ireturn + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 0 4 1 value I + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I + public static int getOne_withCheck(); + descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=2, locals=1, args_size=1 - x: iload_0 - x: iconst_3 - x: iadd + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 4 0 value I - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=1, locals=1, args_size=1 - x: ldc #x // String This value shouldn\'t be seen on the host side. - x: areturn - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=1, locals=1, args_size=1 - x: aload_0 - x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - x: areturn + stack=1, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1020,6 +811,8 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook @@ -1107,6 +900,140 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 5, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Unreachable + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class Compiled from "TinyFrameworkClassWithInitializerDefault.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault @@ -3430,7 +3357,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 5 + interfaces: 0, fields: 2, methods: 1, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -3485,9 +3412,6 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class Compiled from "TinyFrameworkNestedClasses.java" @@ -3568,6 +3492,55 @@ RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 8 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass @@ -3576,7 +3549,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 5 + interfaces: 0, fields: 1, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -3627,15 +3600,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -3793,6 +3764,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -3807,6 +3779,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt index 1b83d244c55c..86a9c65f59b4 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt @@ -216,136 +216,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 2, attributes: 4 - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 3, attributes: 5 - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=3, locals=1, args_size=1 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=0, args_size=0 - x: new #x // class java/lang/RuntimeException - x: dup - x: ldc #x // String Stub! - x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V - x: athrow - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 1, methods: 5, attributes: 3 public int stub; @@ -360,7 +237,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: @@ -449,7 +326,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -462,45 +339,18 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 8, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + interfaces: 0, fields: 0, methods: 2, attributes: 4 + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -514,11 +364,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -529,12 +379,33 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int addOneInner(int); - descriptor: (I)I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -546,11 +417,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC + public static int getOne_withCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -562,11 +433,11 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addTwo(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=3, locals=2, args_size=2 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -577,10 +448,42 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class + Compiled from "TinyFrameworkClassLoadHook.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; + descriptor: Ljava/util/Set; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -594,9 +497,9 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -604,17 +507,18 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow + Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=0, args_size=0 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -626,7 +530,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassW x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +SourceFile: "TinyFrameworkClassLoadHook.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -635,28 +539,27 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class - Compiled from "TinyFrameworkClassLoadHook.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 3, attributes: 3 - public static final java.util.Set<java.lang.Class<?>> sLoadedClasses; - descriptor: Ljava/util/Set; - flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL - Signature: #x // Ljava/util/Set<Ljava/lang/Class<*>;>; + interfaces: 0, fields: 1, methods: 4, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); descriptor: ()V - flags: (0x0002) ACC_PRIVATE + flags: (0x0001) ACC_PUBLIC Code: stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException @@ -670,28 +573,43 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static void onClassLoaded(java.lang.Class<?>); - descriptor: (Ljava/lang/Class;)V - flags: (0x0009) ACC_PUBLIC, ACC_STATIC + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=1, args_size=1 + stack=3, locals=2, args_size=2 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V x: athrow - Signature: #x // (Ljava/lang/Class<*>;)V RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - static {}; - descriptor: ()V - flags: (0x0008) ACC_STATIC + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC Code: - stack=3, locals=0, args_size=0 + stack=3, locals=2, args_size=2 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 x: new #x // class java/lang/RuntimeException x: dup x: ldc #x // String Stub! @@ -703,7 +621,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHo x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassLoadHook.java" +SourceFile: "TinyFrameworkClassWideAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -2153,7 +2071,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 1, attributes: 5 + interfaces: 0, fields: 2, methods: 1, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2199,9 +2117,50 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Stub! + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2211,7 +2170,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 2, attributes: 5 + interfaces: 0, fields: 1, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -2257,15 +2216,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -2406,6 +2363,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -2420,6 +2378,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt index d12a23d3d68f..da434a615c81 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt @@ -611,205 +611,13 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestMembers: com/android/hoststubgen/test/tinyframework/R$Nested -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class - Compiled from "TinyFrameworkCallerCheck.java" -class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl - minor version: 0 - major version: 61 - flags: (0x0020) ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 4 - private static {}; - descriptor: ()V - flags: (0x000a) ACC_PRIVATE, ACC_STATIC - Code: - stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - x: return - - private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); - descriptor: ()V - flags: (0x0002) ACC_PRIVATE - Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String <init> - x: ldc #x // String ()V - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOneKeep(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneKeep - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneKeep - x: ldc #x // String ()I - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; - x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestKeep - - public static int getOneStub(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl - x: ldc #x // String getOneStub - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iconst_1 - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestStub -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class - Compiled from "TinyFrameworkCallerCheck.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck - minor version: 0 - major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - super_class: #x // java/lang/Object - interfaces: 0, fields: 0, methods: 4, attributes: 5 - private static {}; - descriptor: ()V - flags: (0x000a) ACC_PRIVATE, ACC_STATIC - Code: - stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - x: return - - public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); - descriptor: ()V - flags: (0x0001) ACC_PUBLIC - Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String <init> - x: ldc #x // String ()V - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: invokespecial #x // Method java/lang/Object."<init>":()V - x: return - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_withCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String getOne_withCheck - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public static int getOne_noCheck(); - descriptor: ()I - flags: (0x0009) ACC_PUBLIC, ACC_STATIC - Code: - stack=4, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck - x: ldc #x // String getOne_noCheck - x: ldc #x // String ()I - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I - x: ireturn - LineNumberTable: - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -} -InnerClasses: - private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck -SourceFile: "TinyFrameworkCallerCheck.java" -RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub -NestMembers: - com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class - Compiled from "TinyFrameworkClassAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations minor version: 0 major version: 61 flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations super_class: #x // java/lang/Object interfaces: 0, fields: 2, methods: 8, attributes: 3 public int stub; @@ -839,20 +647,20 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V x: return - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations(); + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String <init> x: ldc #x // String ()V x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -869,7 +677,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -884,7 +692,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOne x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -896,7 +704,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 11 6 1 value I RuntimeVisibleAnnotations: x: #x() @@ -912,12 +720,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOneInner x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addOneInner x: ldc #x // String (I)I x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -930,7 +738,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 26 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 26 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -944,7 +752,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String addTwo x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -956,7 +764,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; 11 4 1 value I RuntimeVisibleAnnotations: x: #x() @@ -971,7 +779,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String nativeAddThree x: ldc #x // String (I)I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -997,12 +805,12 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String unsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String unsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; @@ -1028,7 +836,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota flags: (0x0001) ACC_PUBLIC Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations x: ldc #x // String visibleButUsesUnsupportedMethod x: ldc #x // String ()Ljava/lang/String; x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall @@ -1039,7 +847,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations; + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1049,7 +857,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnota x: #x() android.hosttest.annotation.HostSideTestStub } -SourceFile: "TinyFrameworkClassAnnotations.java" +SourceFile: "TinyFrameworkAnnotations.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1062,255 +870,188 @@ RuntimeInvisibleAnnotations: android.hosttest.annotation.HostSideTestClassLoadHook( value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" ) -## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class - Compiled from "TinyFrameworkClassClassWideAnnotations.java" -public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class + Compiled from "TinyFrameworkCallerCheck.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl minor version: 0 major version: 61 - flags: (0x0021) ACC_PUBLIC, ACC_SUPER - this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl super_class: #x // java/lang/Object - interfaces: 0, fields: 3, methods: 9, attributes: 3 - public int stub; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int keep; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public int remove; - descriptor: I - flags: (0x0001) ACC_PUBLIC - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - + interfaces: 0, fields: 0, methods: 4, attributes: 4 private static {}; descriptor: ()V flags: (0x000a) ACC_PRIVATE, ACC_STATIC Code: stack=2, locals=0, args_size=0 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V x: return - public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations(); + private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl(); descriptor: ()V - flags: (0x0001) ACC_PUBLIC + flags: (0x0002) ACC_PRIVATE Code: stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl x: ldc #x // String <init> x: ldc #x // String ()V x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V x: aload_0 x: invokespecial #x // Method java/lang/Object."<init>":()V - x: aload_0 - x: iconst_1 - x: putfield #x // Field stub:I - x: aload_0 - x: iconst_2 - x: putfield #x // Field keep:I x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 15 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public int addOne(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneKeep(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String addOne - x: ldc #x // String (I)I + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: iload_1 - x: invokevirtual #x // Method addOneInner:(I)I + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneKeep + x: ldc #x // String ()I + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: iconst_1 x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 6 1 value I RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep - public int addOneInner(int); - descriptor: (I)I - flags: (0x0001) ACC_PUBLIC + public static int getOneStub(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String addOneInner - x: ldc #x // String (I)I + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl + x: ldc #x // String getOneStub + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iload_1 x: iconst_1 - x: iadd x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 4 1 value I RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public void toBeRemoved(java.lang.String); - descriptor: (Ljava/lang/String;)V - flags: (0x0001) ACC_PUBLIC - Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String toBeRemoved - x: ldc #x // String (Ljava/lang/String;)V - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: new #x // class java/lang/RuntimeException - x: dup - x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V - x: athrow - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 8 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 8 1 foo Ljava/lang/String; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + RuntimeInvisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + android.hosttest.annotation.HostSideTestStub +} +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class + Compiled from "TinyFrameworkCallerCheck.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 4, attributes: 5 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return - public int addTwo(int); - descriptor: (I)I + public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck(); + descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: - stack=4, locals=2, args_size=2 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String addTwo - x: ldc #x // String (I)I + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String <init> + x: ldc #x // String ()V x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iload_1 - x: iconst_2 - x: iadd - x: ireturn + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: return LineNumberTable: LocalVariableTable: Start Length Slot Name Signature - 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - 11 4 1 value I + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck; RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public static int nativeAddThree(int); - descriptor: (I)I + public static int getOne_withCheck(); + descriptor: ()I flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String nativeAddThree - x: ldc #x // String (I)I + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String getOne_withCheck + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: iload_0 - x: iconst_3 - x: iadd + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 4 0 value I RuntimeVisibleAnnotations: x: #x() - com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute - x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - public java.lang.String unsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC - Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String unsupportedMethod - x: ldc #x // String ()Ljava/lang/String; - x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall - x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: ldc #x // String This value shouldn\'t be seen on the host side. - x: areturn - LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 3 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; - RuntimeVisibleAnnotations: - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInStub - x: #x() - com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl - - public java.lang.String visibleButUsesUnsupportedMethod(); - descriptor: ()Ljava/lang/String; - flags: (0x0001) ACC_PUBLIC + public static int getOne_noCheck(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: - stack=4, locals=1, args_size=1 - x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations - x: ldc #x // String visibleButUsesUnsupportedMethod - x: ldc #x // String ()Ljava/lang/String; + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck + x: ldc #x // String getOne_noCheck + x: ldc #x // String ()I x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - x: aload_0 - x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; - x: areturn + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I + x: ireturn LineNumberTable: - LocalVariableTable: - Start Length Slot Name Signature - 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations; RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl } -SourceFile: "TinyFrameworkClassClassWideAnnotations.java" +InnerClasses: + private static #x= #x of #x; // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck +SourceFile: "TinyFrameworkCallerCheck.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub @@ -1319,6 +1060,8 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class Compiled from "TinyFrameworkClassLoadHook.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook @@ -1424,6 +1167,175 @@ RuntimeVisibleAnnotations: RuntimeInvisibleAnnotations: x: #x() android.hosttest.annotation.HostSideTestWholeClassStub +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class + Compiled from "TinyFrameworkClassWideAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 6, attributes: 3 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String <init> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_1 + x: putfield #x // Field stub:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addOne(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String addOne + x: ldc #x // String (I)I + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: iload_1 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public int addTwo(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String addTwo + x: ldc #x // String (I)I + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: iload_1 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: ldc #x // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String unsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker; + x: invokevirtual #x // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class; + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Unreachable + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public java.lang.String visibleButUsesUnsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + x: ldc #x // String visibleButUsesUnsupportedMethod + x: ldc #x // String ()Ljava/lang/String; + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokevirtual #x // Method unsupportedMethod:()Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassStub ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class Compiled from "TinyFrameworkClassWithInitializerDefault.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault @@ -4280,7 +4192,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass super_class: #x // java/lang/Object - interfaces: 0, fields: 2, methods: 2, attributes: 5 + interfaces: 0, fields: 2, methods: 2, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -4350,9 +4262,6 @@ RuntimeVisibleAnnotations: com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class Compiled from "TinyFrameworkNestedClasses.java" @@ -4458,6 +4367,70 @@ RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + minor version: 0 + major version: 61 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V + x: return + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass + x: ldc #x // String <init> + x: ldc #x // String ()V + x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall + x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: bipush 8 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +} +InnerClasses: + public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInStub + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class Compiled from "TinyFrameworkNestedClasses.java" public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass @@ -4466,7 +4439,7 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass super_class: #x // java/lang/Object - interfaces: 0, fields: 1, methods: 3, attributes: 5 + interfaces: 0, fields: 1, methods: 3, attributes: 4 public int value; descriptor: I flags: (0x0001) ACC_PUBLIC @@ -4537,15 +4510,13 @@ public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClass InnerClasses: public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInStub x: #x() com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl -RuntimeInvisibleAnnotations: - x: #x() - android.hosttest.annotation.HostSideTestWholeClassStub NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class Compiled from "TinyFrameworkNestedClasses.java" @@ -4741,6 +4712,7 @@ InnerClasses: public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public static #x= #x of #x; // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + public static #x= #x of #x; // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 SourceFile: "TinyFrameworkNestedClasses.java" RuntimeVisibleAnnotations: @@ -4755,6 +4727,7 @@ NestMembers: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java index 6d8a48a37fea..30dfc80fc60b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java @@ -28,9 +28,9 @@ import android.hosttest.annotation.HostSideTestThrow; @HostSideTestStub @HostSideTestClassLoadHook( "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded") -public class TinyFrameworkClassAnnotations { +public class TinyFrameworkAnnotations { @HostSideTestStub - public TinyFrameworkClassAnnotations() { + public TinyFrameworkAnnotations() { } @HostSideTestStub diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java index 145b65a98d7b..a626bc943018 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java @@ -15,38 +15,21 @@ */ package com.android.hoststubgen.test.tinyframework; -import android.hosttest.annotation.HostSideTestStub; import android.hosttest.annotation.HostSideTestSubstitute; +import android.hosttest.annotation.HostSideTestThrow; import android.hosttest.annotation.HostSideTestWholeClassStub; @HostSideTestWholeClassStub -public class TinyFrameworkClassClassWideAnnotations { - public TinyFrameworkClassClassWideAnnotations() { +public class TinyFrameworkClassWideAnnotations { + public TinyFrameworkClassWideAnnotations() { } public int stub = 1; - public int keep = 2; - - // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime. - public int remove; - - // @Stub public int addOne(int value) { - return addOneInner(value); - } - - // @Keep - public int addOneInner(int value) { return value + 1; } - // @Remove - public void toBeRemoved(String foo) { - throw new RuntimeException(); - } - - @HostSideTestStub @HostSideTestSubstitute(suffix = "_host") public int addTwo(int value) { throw new RuntimeException("not supported on host side"); @@ -56,14 +39,7 @@ public class TinyFrameworkClassClassWideAnnotations { return value + 2; } - @HostSideTestStub - @HostSideTestSubstitute(suffix = "_host") - public static native int nativeAddThree(int value); - - public static int nativeAddThree_host(int value) { - return value + 3; - } - + @HostSideTestThrow public String unsupportedMethod() { return "This value shouldn't be seen on the host side."; } diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java index e1c48bb85563..fec307a3db25 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java @@ -34,6 +34,7 @@ public class TinyFrameworkNestedClasses { return 2; } }; + public Supplier<Integer> getSupplier() { return new Supplier<Integer>() { @Override @@ -52,12 +53,10 @@ public class TinyFrameworkNestedClasses { }; } - @HostSideTestWholeClassStub public class InnerClass { public int value = 5; } - @HostSideTestWholeClassStub public static class StaticNestedClass { public int value = 6; @@ -70,6 +69,10 @@ public class TinyFrameworkNestedClasses { } }; } + + public static class Double$NestedClass { + public int value = 8; + } } public static class BaseClass { diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java index 288c7162aa58..181902a41ec9 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java @@ -21,20 +21,20 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -public class TinyFrameworkClassWithAnnotTest { +public class TinyFrameworkAnnotationsTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void testSimple() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); assertThat(tfc.addOne(1)).isEqualTo(2); assertThat(tfc.stub).isEqualTo(1); } // @Test // public void testDoesntCompile() { -// TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot(); +// TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); // // tfc.addOneInner(1); // Shouldn't compile. // tfc.toBeRemoved("abc"); // Shouldn't compile. @@ -45,19 +45,19 @@ public class TinyFrameworkClassWithAnnotTest { @Test public void testSubstitute() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); assertThat(tfc.addTwo(1)).isEqualTo(3); } @Test public void testSubstituteNative() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); assertThat(tfc.nativeAddThree(1)).isEqualTo(4); } @Test public void testVisibleButUsesUnsupportedMethod() { - TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations(); + TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations(); thrown.expect(RuntimeException.class); thrown.expectMessage("not yet supported"); diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java index 1692c6e89e8c..dda5a0529278 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.android.hoststubgen.test.tinyframework.R.Nested; -import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass; import org.junit.Rule; import org.junit.Test; @@ -88,42 +87,6 @@ public class TinyFrameworkClassTest { } @Test - public void testNestedClass1() { - assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1); - } - - @Test - public void testNestedClass2() { - assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2); - } - - @Test - public void testNestedClass3() { - assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3); - } - - @Test - public void testNestedClass4() { - assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4); - } - - @Test - public void testNestedClass5() { - assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5); - } - - @Test - public void testNestedClass6() { - assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6); - } - - @Test - public void testNestedClass7() { - assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get()) - .isEqualTo(7); - } - - @Test public void testLambda1() { assertThat(new TinyFrameworkLambdas().mSupplier.get()).isEqualTo(1); } @@ -220,18 +183,13 @@ public class TinyFrameworkClassTest { } @Test - public void testMethodCallBeforeSuperCall() { - assertThat(new SubClass(3).value).isEqualTo(3); - } - - @Test public void testClassLoadHook() { assertThat(TinyFrameworkClassWithInitializerStub.sInitialized).isTrue(); // Having this line before assertThat() will ensure these class are already loaded. var classes = new Class[]{ TinyFrameworkClassWithInitializerStub.class, - TinyFrameworkClassAnnotations.class, + TinyFrameworkAnnotations.class, TinyFrameworkForTextPolicy.class, }; diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java new file mode 100644 index 000000000000..83753b5b1fb2 --- /dev/null +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java @@ -0,0 +1,96 @@ +/* + * 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.hoststubgen.test.tinyframework; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class TinyFrameworkClassWideAnnotationsTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSimple() { + var tfc = new TinyFrameworkClassWideAnnotations(); + assertThat(tfc.addOne(1)).isEqualTo(2); + assertThat(tfc.stub).isEqualTo(1); + } + + @Test + public void testSubstitute() { + var tfc = new TinyFrameworkClassWideAnnotations(); + assertThat(tfc.addTwo(1)).isEqualTo(3); + } + + @Test + public void testVisibleButUsesUnsupportedMethod() { + var tfc = new TinyFrameworkClassWideAnnotations(); + + thrown.expect(RuntimeException.class); + thrown.expectMessage("not yet supported"); + tfc.visibleButUsesUnsupportedMethod(); + } + + @Test + public void testMethodCallBeforeSuperCall() { + assertThat(new TinyFrameworkNestedClasses.SubClass(3).value).isEqualTo(3); + } + + @Test + public void testNestedClass1() { + assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1); + } + + @Test + public void testNestedClass2() { + assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2); + } + + @Test + public void testNestedClass3() { + assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3); + } + + @Test + public void testNestedClass4() { + assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4); + } + + @Test + public void testNestedClass5() { + assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5); + } + + @Test + public void testNestedClass6() { + assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6); + } + + @Test + public void testNestedClass7() { + assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get()) + .isEqualTo(7); + } + + @Test + public void testNestedClass8() { + assertThat(new TinyFrameworkNestedClasses.StaticNestedClass.Double$NestedClass().value) + .isEqualTo(8); + } +} diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt index 6b46c84bd0bb..5b2795c4cff2 100644 --- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt +++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt @@ -23,18 +23,6 @@ import org.objectweb.asm.Opcodes.ACC_PUBLIC import org.objectweb.asm.Opcodes.ACC_STATIC class AsmUtilsTest { - private fun checkGetDirectOuterClassName(input: String, expected: String?) { - assertThat(getDirectOuterClassName(input)).isEqualTo(expected) - } - - @Test - fun testGetDirectOuterClassName() { - checkGetDirectOuterClassName("a", null) - checkGetDirectOuterClassName("a\$x", "a") - checkGetDirectOuterClassName("a.b.c\$x", "a.b.c") - checkGetDirectOuterClassName("a.b.c\$x\$y", "a.b.c\$x") - } - @Test fun testVisibility() { fun test(access: Int, expected: Visibility) { |